It has conceptual similarities to CORBA, but it is intended to extend it in all ways that are required for real time multimedia operations.
It provides a multimedia object model, which can be used for both: communication between components in one address space (one process), and between components that are in different threads, processes or on different hosts.
All in all, it will be designed for extremely high performance (so everything shall be optimized to be blazingly fast), suitable for very communicative multimedia applications. For instance streaming videos around is one of the applications of MCOP, where most CORBA implementations would go down to their knees.
The interface definitions can handle the following natively:
Continuous streams of data (such as audio data).
Event streams of data (such as MIDI events).
Real reference counting.
and the most important CORBA gimmicks, like
Synchronous method invocations.
Asynchronous method invocations.
Constructing user defined data types.
Multiple inheritance.
Passing object references.
Design goals/ideas:
Marshalling should be easy to implement.
Demarshalling requires the receiver to know what type they want to demarshall.
The receiver is expected to use every information - so skipping is only in the protocol to a degree that:
If you know you are going to receive a block of bytes, you don't need to look at each byte for an end marker.
If you know you are going to receive a string, you don't need to read it until the zero byte to find out it's length while demarshalling, however,
If you know you are going to receive a sequence of strings, you need to look at the length of each of them to find the end of the sequence, as strings have variable length. But if you use the strings for something useful, you'll need to do that anyway, so this is no loss.
As little overhead as possible.
Marshalling of the different types is show in the table below:
Type | Marshalling Process | Result |
---|---|---|
void | void types are marshalled by omitting them, so nothing is written to the stream for them. | |
long | is marshalled as four bytes, the most significant byte first, so the number 10001025 (which is 0x989a81) would be marshalled as: | 0x00 0x98 0x9a 0x81 |
enums | are marshalled like longs | |
byte | is marshalled as a single byte, so the byte 0x42 would be marshalled as: | 0x42 |
string | is marshalled as a long, containing the length of the following string, and then the sequence of characters strings must end with one zero byte (which is included in the length counting). Importantinclude the trailing 0 byte in length counting! “hello” would be marshalled as: | 0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00 |
boolean | is marshalled as a byte, containing 0 if false or 1 if true, so the boolean value true is marshalled as: | 0x01 |
float | is marshalled after the four byte IEEE754 representation - detailed docs how IEEE works are here: http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html and here: http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html. So, the value 2.15 would be marshalled as: | 0x9a 0x99 0x09 0x40 |
struct | A structure is marshalled by marshalling it's contents. There are no additional prefixes or suffixes required, so the structure struct test { string name; // which is "hello" long value; // which is 10001025 (0x989a81) }; would be marshalled as |
0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x98 0x9a 0x81 |
sequence | a sequence is marshalled by listing the number of elements that follow, and then marshalling the elements one by one. So a sequence of 3 longs a, with a[0] = 0x12345678, a[1] = 0x01 and a[2] = 0x42 would be marshalled as: |
0x00 0x00 0x00 0x03 0x12 0x34 0x56 0x78 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x42 |
If you need to refer to a type, all primitive types are referred by the
names given above. Structures and enums get own names (like
Header). Sequences are referred as *normal
type
, so that a sequence of longs is “*long”
and a sequence of Header struct's is “*Header”.
The MCOP message header format is defined as defined by this structure:
struct Header { long magic; // the value 0x4d434f50, which is marshalled as MCOP long messageLength; long messageType; };
The possible messageTypes are currently
mcopServerHello = 1 mcopClientHello = 2 mcopAuthAccept = 3 mcopInvocation = 4 mcopReturn = 5 mcopOnewayInvocation = 6
A few notes about the MCOP messaging:
Every message starts with a Header.
Some messages types should be dropped by the server, as long as the authentication is not complete.
After receiving the header, the protocol (connection) handling can receive the message completely, without looking at the contents.
The messageLength in the header is of course in some cases redundant, which means that this approach is not minimal regarding the number of bytes.
However, it leads to an easy (and fast) implementation of non-blocking messaging processing. With the help of the header, the messages can be received by protocol handling classes in the background (non-blocking), if there are many connections to the server, all of them can be served parallel. You don't need to look at the message content, to receive the message (and to determine when you are done), just at the header, so the code for that is pretty easy.
Once a message is there, it can be demarshalled and processed in one single pass, without caring about cases where not all data may have been received (because the messageLength guarantees that everything is there).
To call a remote method, you need to send the following structure in the body of an MCOP message with the messageType = 1 (mcopInvocation):
struct Invocation { long objectID; long methodID; long requestID; };
after that, you send the parameters as structure, for example, if you invoke the method string concat(string s1, string s2), you send a structure like
struct InvocationBody { string s1; string s2; };
if the method was declared to be oneway - that means asynchronous without return code - then that was it. Otherwise, you'll receive as answer the message with messageType = 2 (mcopReturn)
struct ReturnCode { long requestID; <resulttype> result; };
where <resulttype> is the type of the result. As void types are omitted in marshalling, you can also only write the requestID if you return from a void method.
So our string concat(string s1, string s2) would lead to a returncode like
struct ReturnCode { long requestID; string result; };
To do invocations, you need to know the methods an object supports. To do so, the methodID 0, 1, 2 and 3 are hardwired to certain functionalities. That is
long _lookupMethod(MethodDef methodDef); // methodID always 0 string _interfaceName(); // methodID always 1 InterfaceDef _queryInterface(string name); // methodID always 2 TypeDef _queryType(string name); // methodID always 3
to read that, you of course need also
struct MethodDef { string methodName; string type; long flags; // set to 0 for now (will be required for streaming) sequence<ParamDef> signature; }; struct ParamDef { string name; long typeCode; };
the parameters field contains type components which specify the types of the parameters. The type of the returncode is specified in the MethodDef's type field.
Strictly speaking, only the methods
_lookupMethod()
and
_interfaceName()
differ from object to object,
while the _queryInterface()
and
_queryType()
are always the same.
What are those methodIDs? If you do an MCOP invocation, you are expected to pass a number for the method you are calling. The reason for that is, that numbers can be processed much faster than strings when executing an MCOP request.
So how do you get those numbers? If you know the signature of the method, that is a MethodDef that describes the method, (which contains name, type, parameter names, parameter types and such), you can pass that to _lookupMethod of the object where you wish to call a method. As _lookupMethod is hardwired to methodID 0, you should encounter no problems doing so.
On the other hand, if you don't know the method signature, you can find which methods are supported by using _interfaceName, _queryInterface and _queryType.
Would you like to comment or contribute an update to this page?
Send feedback to the TDE Development Team