MCOP protokolspecifikation

Indledning

Den har begrebsmæssige ligheder med CORBA, men er beregnet til at kunne udvides på alle måder som kræves for multimediaoperationer i realtid.

Den sørger for en multimediaobjektmodel, som både kan bruges til kommunikation mellem komponenter i et adresserum (en proces), og mellem komponenter som findes i forskellige tråde, processer eller på forskellige værtsmaskiner.

Totalt set, vil den blive konstrueret til ekstremt høj ydelse (så alt skal optimeres for at være voldsomt hurtigt), passende for meget kommunikationsintensive multimedieprogrammer. At for eksempel sende video rundt er en af tilpasningerne for MCOP, hvor de fleste CORBA-implementationer ville gå i knæ.

Grænsefladedefinitionerne kan håndtere følgende selv:

  • Kontinuerlige datastrømme (såsom lyddata).

  • Begivenhedsstrømme af data (såsom MIDI-begivenheder).

  • Rigtig referenceregning.

og de vigtigste CORBA-kneb, såsom

  • Synkrone metodekald.

  • Asynkrone metodekald.

  • Konstruere brugerdefinerede datatyper.

  • Multipel arv.

  • Sende objektreferencer.

MCOP's meddelelseskodning

Konstruktionsmål/idéer:

  • Kodning skal være enkelt at implementere.

  • Afkodning kræver at modtageren véd hvilken type som skal afkodes.

  • Modtageren forventes at bruge al information, så mulighed for at hoppe over data findes kun i protokollen i det omfang at:

    • Hvis man ved at en blok af data bliver modtaget, behøver man ikke kigge på hver del efter en slutmarkør.

    • Hvis man ved at en streng vil blive modtaget, behøver man ikke læse til en null-byte for at finde ud af dens længde ved afkodning, men,

    • Hvis man véd at en sekvens af strenge bliver modtaget skal man kigge på længden for hver af dem for at finde ud af slutningen af sekvensen, eftersom strenge har variabel længde. Men hvis strengene bruges til noget nyttigt, skal det gøres i alle tilfælde, så dette er ikke noget tab.

  • Så lidt ekstra omkostning som muligt.

Kodningen af de forskellige typer vises i tabellen nedenfor:

TypeKodningsprocesResultat
voidtypen void kodes ved at udelade den, så ingenting skrives til strømmen for den. 
longkodes som fire byte, med den mest signifikante først, så tallet 10001025 (som er 0x989a81) vil blive kodet som:0x00 0x98 0x9a 0x81
nummereringstyper

kodes som long

 
byte

kodes som en enkelt byte, så 0x42 vil blive kodet som:

0x42
streng

kodes som en long, som indeholder længden på den efterfølgende streng, og derefter sekvensen af tegn. Strenge skal slutte med en null-byte (som indgår i længdeberegningen).

Vigtigt

inklusive den sidste 0 byte i længderegningen!

hello” ville blive kodet som:

0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00
boolean

kodes som en byte, som indeholder 0 hvis false eller 1 hvis true, så at den booleske værdi true kodes som:

0x01
float

kodes med fire-byte IEEE754 repræsentationen, detaljeret dokumentation om hvordan IEEE fungerer findes her: http://twister.ou.edu/workshop.docs/ common-tools/ numerical_comp_guide/ ncg_math.doc.html og her: http://java.sun.com/docs/books/ vmspec/ 2nd-edition/ html/ Overview.doc.html. Så værdien 2,15 ville blive kodet som:

0x9a 0x99 0x09 0x40
struct

En struktur kodes ved at kode dens indhold. Der kræves ingen yderligere præfiks eller suffiks, så strukturen

struct test {
    string name;        // som er "hello"
    long value;         // som er 10001025  (0x989a81)
};

ville blive kodet som:

0x00 0x00 0x00 0x06 0x68 0x65 0x6c 0x6c 0x6f 0x00 0x00 0x98 0x9a 0x81

sekvens

En sekvens kodes ved at lave en liste over antallet af elementer som følger, og derefter kode elementerne en efter en.

Så en sekvens med tre "long a", med a[0] = 0x12345678, a[1] = 0x01 og a[2] = 0x42 ville blive kodet som

0x00 0x00 0x00 0x03 0x12 0x34 0x56 0x78 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x42

Hvis du har brug for at henvise til en type, angives alle primitive typer med navnene som findes ovenfor. Strukturer og nummereringstyper får deres egne navne (som Header). Sekvenser benævnes som *normal type, så en sekvens af long er “*long” og en sekvens af Header struct'er er “*Header”.

Meddelelser

Formatet på MCOP's meddelelseshovede defineres af denne struktur:

struct Header {
    long magic;          // værdien 0x4d434f50, som kodes som MCOP
    long messageLength;
    long messageType;
};

De mulige meddelelsetyper er for øjeblikket

mcopServerHello                = 1
 mcopClientHello                = 2
 mcopAuthAccept                        = 3
 mcopInvocation                        = 4
 mcopReturn                                = 5
 mcopOnewayInvocation   = 6

Lidt information om MCOP's meddelelseshåndtering:

  • Hver meddelelse begynder med et hoved.

  • Visse meddelelsestyper skal ignoreres af serveren, indtil godkendelsen er færdig.

  • Efter at hovedet er modtaget, kan protokolhåndteringen (forbindelsen) tage imod meddelelsen i sin helhed, uden at kigge på indholdet.

Meddelelseslængden i hovedet er naturligvis sommetider redundant, hvilket gør at metoden ikke altid er minimal med hensyn til antal byte.

Dette giver dog en enkel (og hurtig) implementation for behandling af meddelelser som ikke blokerer. Ved hjælp af hovedet kan meddelelser modtages af protokolhåndteringsklasser i baggrunden (uden blokering), hvis der er mange forbindelser til serveren kan alle behandles parallelt. Man behøver ikke kigge på meddelelsens indhold for at tage imod en meddelelse (og afgøre når man er klar), kun på hovedet, så koden for dette er ganske enkel.

Når en meddelelse er på plads, kan den afkodes og behandles i et enkelt skridt, uden at bryde sig om tilfælde hvor al data måske ikke er modtaget (eftersom meddelelseslængden garanterer at alt er på plads).

Kald

For at kalde en fjernmetode, skal man sende følgende struktur i en MCOP-meddelelse med messageType = 1 (mcopInvocation):

struct Invocation {
    long objectID;
    long methodID;
    long requestID;
};

derefter sendes parametrene som en struktur, f.eks. hvis man kalder metoden "string concat(string s1, string s2)", sendes strukturen

struct InvocationBody {
    string s1;
    string s2;
};

hvis metoden deklareredes som envejs, hvilket betyder asynkront uden returværdi, er det alt. Ellers får man en meddelelse som svar med messageType = 2 (mcopReturn).

struct ReturnCode {
    long requestID;
    <resulttype> result;
};

hvor <resulttype> er typen for resultatet. Eftersom void-typer udelades ved kodning, kan man også kun angive requestID hvis man returnerer fra en void-metode.

Så vor "string concat(string s1, string s2)" ville give en returkode som

struct ReturnCode {
    long   requestID;
    string result;
};

Inspektér grænseflader

For at lave kald, skal man kende til metoderne som et objekt understøtter. For at gøre dette, er methodID 0, 1, 2 og 3 hårdkodede for visse funktioner. Det vil sige

long _lookupMethod(MethodDef methodDef);                                // metode-id altid 0
string _interfaceName();                                                                // metod-id altid 1
InterfaceDef _queryInterface(string name);                                // metodie-id altid 2
TypeDef _queryType(string name);                                                // metodie-id altid 3

for at læse dette, behøver du naturligvis også

struct MethodDef {
        string  methodName;
        string  type;
        long    flags;        // nu sat til 0 (kræves for strømme)
        sequence<ParamDef> signature;
};

struct ParamDef {
        string name;
        long   typeCode;
};

parameterfeltet indeholder typekomponenter som angiver typerne for parametrene. Typen for returkoden angives i typefeltet for MethodDef.

Hvis man skal være streng, er det kun metoderne _lookupMethod() og _interfaceName() som varierer fra objekt til objekt, mens _queryInterface() og _queryType() altid er ens.

Hvad er en sådan her methodID? Hvis man laver et MCOP-kald, forventes man at sende nummeret for metoden som kaldes. Årsagen til dette er at numre kan behandles meget hurtigere end strenge når en MCOP-forespørgsel køres.

Så hvordan skaffer man sig et sådan nummer? Hvis man kender til signaturen for metoden, dvs. en MethodDef som beskriver den (som indeholder navn, type, parameternavn, parametertyper og lignende), så kan man sende den til _lookupMethod for objektet hvor man vil kalde en metode. Eftersom _lookupMethod er hårdkodet til methodID 0, skulle det ikke være nogen problem at gøre dette.

Hvis man på den anden side ikke kender til metodens signatur, kan man finde hvilke metoder der understøttes ved at bruge _interfaceName, _queryInterface og _queryType.

Typedefinitioner

Brugerdefinerede datatyper beskrives med strukturen TypeDef:

struct TypeComponent {
        string type;
        string name;
};

struct TypeDef {
        string name;

        sequence<TypeComponent> contents;
};