CORBA GIOP消息格式学习
想要深入理解ORB的工作過程與原理,學習與了解GIOP消息格式必不可少。我們知道GIOP是獨立于具體通信的更高級別的抽象,因此這里針對GIOP在TCP/IP上的實現IIOP協議進行學習與分析(IIOP是規范中要求所有的ORB廠商都必要實現的協議,因此GIOP規范中也對IIOP協議進行了具體的定義)。
根據CORBA Specification的相關闡述,GIOP規范主要包括了以下三個方面:
1、Common Data Representation(CDR) definition(公共數據表達定義),CDR定義了IDL數據類型序列化為底層的byte流的規范。
2、GIOP Message Formats(GIOP消息格式),GIOP消息是ORB之間的通信最小單元。GIOP消息的類型有對象請求,對象定位和通信連接管理等等。
3、GIOP Transport Assumptions(GIOP傳輸層注意項,不知道這里翻譯成”注意項“是否合適)。GIOP規范描述了一些底層協議的實現在傳輸GIOP消息時需要關注的事項,GIOP給出了一些假設/建議,另外規范還描述了應該如何去管理連接(建立與斷開),保證GIOP消息順序等等。
通常情況下我們并不會去關注第三點。
好了,我們一點一點學習以上三點(大部分情況下我只是作為規范的搬運工而已)。
Common Data Representation(CDR):
CDR描述了IDL數據類型應該如何序列化。包括了以下幾點:
1、Byte Ordering,也就是我們所說的大端小端,這里不多陳述大端小端的知識了,讀者如有不懂可以自行baidu/google之。每個GIOP消息/Encapsulation(Encapsulation的闡述請點這里)都包含了一個用于標識大小端的標識。
2、Aligned Primitive Types基本類型(如int, short, string)的對齊,就像我們在c/c++接觸到的內存對齊情況一樣,如此可以使得我們更快地序列化/反序列化消息。
3、Complete OMG IDL Mapping完整的IDL類型映射:CDR描述了所有IDL類型的底層表示,除了基本類型以外,還包括了可以傳輸的pseudo-object(偽對象)比如TypeCodes(用于在DII和Any中表示參數的類型信息),Object Reference和異常等等(說它們是偽對象,是因為它們并不是真實的對象,而是對象的另一種表達,比如Object Reference可以用來傳送一個對象,但Object Reference只是一個IOR)。
首先來看一下第二點,基本類型的序列化。
首先在邊界對齊上,數據類型必須對齊到它的大小的倍數的位置上。比如依次序列化下面的struct:
module alignmenttest{struct TestStruct{short sa;short sb;char ca;char cb;long la; } } octet index-------------------01 short sa-------------------23 short sb-------------------4 char ca-------------------5 char cb-------------------67-------------------8910 long la11-------------------這與我們的C語言在大部分機器的上的內存對齊規則是一致的。因此GIOP甚至可以不經過任何處理直接搬到內存中去就可以操作,使用。
Encapsulation
這里不知道怎么去翻譯這個詞,也許用”封裝“比較合適吧,它就像是我們C語言中的struct,java中的class,代表一個集合,并且可以相互嵌套。GIOP規范這里所指的Encapsulation,還有以下兩個含義:
1、Encapsulation與Encapsulation,Message之間的Byte Ordering可以是不相同的,即使它們之間是嵌套關系。
2、Encapsulation是一個抽象的表示,通常具體地它將會被編碼成sequence<octet>,編碼成sequence<octet>時,第一個octet代表著Byte Ordering。
Value Types
valuetype類似于一個加強版本的struct,但valuetype支持繼承并且可以定義本地方法(包括構造函數)等而struct不支持,詳細請看CORBA規范的說明。基于上面這些特性,在傳輸valuetype時候,需要包含valuetype的基類信息以便客戶端進行向上轉類等操作。在進行序列化的時候,需要首先序列化基類的元素,再序列化子類的元素。valuetype支持自定義序列化,但客戶端和服務端的一致性由用戶自己保證。需要注意的是,valuetype中的方法是作用于本地的,它并不會像interface中的方法論一樣被傳遞到遠程Servant。
Pseudo-Object Types 偽對象類型
這里主要說一下TypeCode,關于Object Reference的說明在后面IIOP消息中會有說明。TypeCode的作用上面已經提到,這里再重復一遍,TypeCode提供了一塊數據中各個成員的信息,一般在確定動態類型Any的時候會用到。比如:
module...{interface...{void add(in long a, n long b, Any c);} }在運行時Any c的具體類型才可以得知,那么在傳輸c的時候,需要附上c的序列化結構信息,服務端才能正確地反序列化/還原c,我們可以看一下Any類型的序列化過程相關代碼:
public class CDROutputStream_1_0 extends CDROutputStreamBase {....public void write_any(Any any) {if (any == null) {throw wrapper.nullParam();}write_TypeCode(any.type());//首先寫出TypeCode信息any.write_value(parent);//寫出值信息。}.... }下面是寫出TypeCode信息的相關代碼,它根據當前any類型的_kind來寫出相對應的信息。比如對應于struct類型,那么寫出的TypeCode信息將包括struct中每個成員的類型信息,這可能是為了應對IDL在客戶端與服務端不致的問題。
public final class TypeCodeImpl extends TypeCode {....public void write_value(TypeCodeOutputStream tcos) {...TypeCodeOutputStream topStream = tcos.getTopLevelStream();if (_kind == tk_indirect) {// The encoding used for indirection is the same as that used for recursive ,// TypeCodes i.e., a 0xffffffff indirection marker followed by a long offset// (in units of octets) from the beginning of the long offset.int pos = topStream.getPositionForID(_id);int topPos = tcos.getTopLevelPosition();tcos.writeIndirection(tk_indirect, pos);return;}tcos.write_long(_kind);// Bug fix 5034649:// Do this AFTER the write of the _kind in case the alignment// for the long changes the position.topStream.addIDAtPosition(_id, tcos.getTopLevelPosition()-4);switch (typeTable[_kind]) {case EMPTY:// nothing more to marshalbreak;case SIMPLE:switch (_kind) {case TCKind._tk_string:case TCKind._tk_wstring:// marshal the bound on string length tcos.write_long(_length);break;case TCKind._tk_fixed:tcos.write_ushort(_digits);tcos.write_short(_scale);break;default:// unknown typecode kindthrow wrapper.invalidSimpleTypecode() ;}break;case COMPLEX:TypeCodeOutputStream _encap = tcos.createEncapsulation(tcos.orb());switch(_kind) {case TCKind._tk_objref:case TCKind._tk_abstract_interface:_encap.write_string(_id);_encap.write_string(_name);break;case TCKind._tk_union:_encap.write_string(_id);_encap.write_string(_name);_discriminator.write_value(_encap);_encap.write_long(_defaultIndex);_encap.write_long(_memberCount);for (int i=0; i < _memberCount; i++) {if (i == _defaultIndex) {_encap.write_octet(_unionLabels[i].extract_octet());} else {switch (realType(_discriminator).kind().value()) {case TCKind._tk_short:_encap.write_short(_unionLabels[i].extract_short());break;case TCKind._tk_long:_encap.write_long(_unionLabels[i].extract_long());break;case TCKind._tk_ushort:_encap.write_short(_unionLabels[i].extract_ushort());break;case TCKind._tk_ulong:_encap.write_long(_unionLabels[i].extract_ulong());break;case TCKind._tk_float:_encap.write_float(_unionLabels[i].extract_float());break;case TCKind._tk_double:_encap.write_double(_unionLabels[i].extract_double());break;case TCKind._tk_boolean:_encap.write_boolean(_unionLabels[i].extract_boolean());break;case TCKind._tk_char:_encap.write_char(_unionLabels[i].extract_char());break;case TCKind._tk_enum:_encap.write_long(_unionLabels[i].extract_long());break;case TCKind._tk_longlong:_encap.write_longlong(_unionLabels[i].extract_longlong());break;case TCKind._tk_ulonglong:_encap.write_longlong(_unionLabels[i].extract_ulonglong());break;case TCKind._tk_wchar:_encap.write_wchar(_unionLabels[i].extract_wchar());break;default:throw wrapper.invalidComplexTypecode() ;}}_encap.write_string(_memberNames[i]);_memberTypes[i].write_value(_encap);}break;case TCKind._tk_enum:_encap.write_string(_id);_encap.write_string(_name);_encap.write_long(_memberCount);for (int i=0; i < _memberCount; i++) {_encap.write_string(_memberNames[i]);}break;case TCKind._tk_sequence:lazy_content_type().write_value(_encap);_encap.write_long(_length);break;case TCKind._tk_array:_contentType.write_value(_encap);_encap.write_long(_length);break;case TCKind._tk_alias:case TCKind._tk_value_box:_encap.write_string(_id);_encap.write_string(_name);_contentType.write_value(_encap);break;case TCKind._tk_struct:case TCKind._tk_except:_encap.write_string(_id);_encap.write_string(_name);_encap.write_long(_memberCount);for (int i=0; i < _memberCount; i++) {_encap.write_string(_memberNames[i]);_memberTypes[i].write_value(_encap);}break;case TCKind._tk_value:_encap.write_string(_id);_encap.write_string(_name);_encap.write_short(_type_modifier);if (_concrete_base == null) {_orb.get_primitive_tc(TCKind._tk_null).write_value(_encap);} else {_concrete_base.write_value(_encap);}_encap.write_long(_memberCount);for (int i=0; i < _memberCount; i++) {_encap.write_string(_memberNames[i]);_memberTypes[i].write_value(_encap);_encap.write_short(_memberAccess[i]);}break;default:throw wrapper.invalidTypecodeKindMarshal() ;}// marshal the encapsulation _encap.writeOctetSequenceTo(tcos);break;}}... }Object Reference,對象引用
傳送對象引用一般情況下傳送它的IOR就可以了,IOR至少包含了以下信息:
1、版本信息,以保證低版本的ORB不會亂去解析高版本的IOR表示。
2、傳輸層面對象宿主機器的地址。
3、對象ID,用于唯一定位目標對象 。
Abstract Interface,抽象接口
Abstract Interface是一種比較特殊的類型,它是一個方法集合,interface和valuetype類型可以繼承它,比如:
abstract interface Describable { string get_description(); }; interface Example { void display (in Describable anObject); }; interface Account : Describable {// passed by reference // add Account methods here }; valuetype Currency supports Describable {// passed by value // add Currency methods here };如上面,如果在運行時anObject參數是Account,那么在序列化的時候,首先用一個boolean為true的值以表明它是一個Object Reference,然后緊接著這個對象的IOR。
---------------TRUE ---------------IOR ---------------如果在運行時anObject參數是Currency,那么在序列化的時候,首先用一個boolean為false值以表明它是一個valuetype,然后緊接著它的值。
---------------FALSE ---------------Value ---------------GIOP消息格式
首先我們來看一下GIOP消息的IDL聲明:
module GIOP { // IDL extended for version 1.1 and 1.2 struct Version {octet major;octet minor;};#ifndef GIOP_1_1// GIOP 1.0enum MsgType_1_0 { // Renamed from MsgType Request, Reply, CancelRequest,LocateRequest, LocateReply,CloseConnection, MessageError};#else// GIOP 1.1enum MsgType_1_1 {Request, Reply, CancelRequest,LocateRequest, LocateReply,CloseConnection, MessageError,Fragment// GIOP 1.1 addition };#endif // GIOP_1_1// GIOP 1.0struct MessageHeader_1_0 { // Renamed from MessageHeaderchar magic [4];Version GIOP_version;boolean byte_order;octet message_type;unsigned long message_size;};// GIOP 1.1 struct MessageHeader_1_1 {char magic [4];Version GIOP_version;octet flags;// GIOP 1.1 change octet message_type;unsigned long message_size;};// GIOP 1.2 typedef MessageHeader_1_1 MessageHeader_1_2; };magic表明這是一個GIOP消息,它的值為字符串GIOP的byte:"GIOP".getBytes("ISO8859-1")
GIOP_Version包含這個消息所使用的GIOP協議的版本信息,需要注意的是,它和IIOP版本號是兩碼事,雖然它們的IDL描述是一樣的
byte_order只在GIOP 1.0版本中被使用,它指定后面元素如message_size(比如占據4個字節整形)是大端表示還是小端表示的。
flags在GIOP 1.1和1.2版本中使用,它和byte_ordfer一樣都只占據一個字節,只不過flag中用bit表示一些信息:
|7|6|5|4|3|2|1|0|| | | | | | | || | | | | | | ->byte order, 0: big-endian, 1: little-endian| | | | | | ---->后面是否還有fragments, 0: No, 1: Yes---------------->保留message_type,它的值表明了消息的類型,下面是消息類型的列表。
| Request | Client | 0 | 1.0, 1.1, 1.2 |
| Reply | Server | 1 | 1.0, 1.1, 1.2 |
| CancleRequest | Client | 2 | 1.0, 1.1, 1.2 |
| LocateRequest | Client | 3 | 1.0, 1.1, 1.2 |
| LocateReply | Server | 4 | 1.0, 1.1, 1.2 |
| CloseConnection | Server | 5 | 1.0, 1.1, 1.2 |
| MessageError | Both | 6 | 1.0, 1.1, 1.2 |
| Fragment | Both | 7 | 1.1, 1.2 |
message_size表明消息頭后面的還有多個字節 (消息體Message Body的長度,不包括12個字節的消息頭),這是一個unsigned long類型,它的字節順序與上面byte_order域與flags域所指定的字節順序是一致的。注意對于GIOP1.2,message_size + 12(消息頭長度)的和一定需要可以被8整除(也許為了內存對齊)。
下面介紹一下消息類型,因為篇幅原因,這里只對Request Message和Reply Message介紹。
Request Message
Request Header的IDL定義:
module GIOP { // IDL extended for version 1.1 and 1.2// GIOP 1.0struct RequestHeader_1_0 { // Renamed from RequestHeader IOP::ServiceContextListservice_context;unsigned long request_id;boolean response_expected;sequence <octet> object_key;string operation;Principal requesting_principal;};// GIOP 1.1 struct RequestHeader_1_1 {IOP::ServiceContextList service_contextunsigned long request_id;octet reserved[3];sequence<octet> object_key;string operation;Principal requesting_principal;};// GIOP 1.2typedef short AddressingDisposition;const short KeyAddr=0;const short ProfileAddr=1;const short ReferenceAddr=2;struct IORAddressingInfo {unsigned long selected_profile_index;IOP::IOR ior;};union TargetAddress switch (AddressingDisposition) {case KeyAddr: sequence<octet> object_key;case ProfileAddr: IOP::TaggedProfile profile;case ReferenceAddr: IORAddressingInfo ior;};struct RequestHeader_1_2 {unsigned long request_id;octet response_flags;octet reserved[3];TargetAddress target;string operation;IOP::ServiceContextList service_context;// Principal not in GIOP 1.2 }; };request_id用于將服務器的響應信息與客戶端發送的請求信息對應起來,因為客戶端的同時可能向服務端發送多個對象的請求,而它們可以共用一個連接,服務器返回的信息將包含客戶端發送的request_id,客戶端利用request_id來完成響應信息的分發。
response_flags用于表示此請求是否需要返回值,它的最低位是1的話表示需要返回值,如何需要返回值,并且在DII中INV_NO_RESPONSE標志位沒有被設置,那么response_flags必須設置為0x03。
如果此請求不需要返回值,或者在DII調用中設置了INV_NO_RESPONSE標志位,那么response_flags可以設置為0x00或者0x01
reserved的值應該設置為,它為未來保留使用。
object_key,在GIOP 1.0和1.1中,用于標識服務端的目標對象,它其實就是IOR IIOPProfile中的object_key的值。
target,但從GIOP 1.2開始,更換為使用target域來標識調用的目標。
operation是idl中定義的interface中的目標方法名。
service_context包含了從客戶端傳遞到服務端的Service Context信息。
requesting_principal,在GIOP 1.0和GIOP 1.1版本中,它包含了關于客戶端身份信息的類,用于訪問控制和其它目的。在它已經被棄用,并且在GIOP 1.2中不再包含這部分信息。
Request Body
在GIOP 1.0和1.1中,Request Body被序列化成Encapsulation并立即被附在消息頭的后面。從GIOP 1.2開始,Request Body的開始位置是對齊到8字節的,如果消息頭被修改之后,Request Body不需要重新序列化。Request包括了以下幾項內容(按順序序列化):
1、所有的in和inout參數,按照它們在方法參數中出現的先后順序(從左到右)。
2、可選的Context pseudo object(Context偽對象),這些Context pseudo object被序列化成sequence<String>,每個Context pseudo object被序列化成一對String,Property Name和Property Value。只有操作的IDL定義包含有一個Conext Expression的情況下才會出現Context pseudo object,并且只會包含Context Expression中聲明的成員。
比如下面方法的
double example(in short m, out string str, inout p)它的Request Body的將會是下面的結構:
struct example_body{short m;long p; }Reply Message
只有Request Message中的response expected位被設置為TRUE的情況下,服務器才會發送對應的Reply Message。Reply Message包括了所有的inout和out參數,并且可以包含有異常值。同樣Reply Message包括了Reply Header和Reply Body兩部分。
Reply Header
首先來看一下它的IDL定義:
module GIOP { // IDL extended for 1.2 #ifndef GIOP_1_2// GIOP 1.0 and 1.1enum ReplyStatusType_1_0 { // Renamed from ReplyStatusType NO_EXCEPTION,USER_EXCEPTION,SYSTEM_EXCEPTION,LOCATION_FORWARD};// GIOP 1.0struct ReplyHeader_1_0 { // Renamed from ReplyHeader IOP::ServiceContextList service_context;unsigned long request_id;ReplyStatusType_1_0 reply_status;};// GIOP 1.1 typedef ReplyHeader_1_0 ReplyHeader_1_1;// Same Header contents for 1.0 and 1.1 #else// GIOP 1.2enum ReplyStatusType_1_2 {NO_EXCEPTION,USER_EXCEPTION,SYSTEM_EXCEPTION,LOCATION_FORWARD,LOCATION_FORWARD_PERM,// new value for 1.2NEEDS_ADDRESSING_MODE // new value for 1.2 };struct ReplyHeader_1_2 {unsigned long request_id;ReplyStatusType_1_2 reply_status;IOP:ServiceContextList service_context}; #endif // GIOP_1_2 };request_id用于把Reply Message與對應的Request Message關聯起來,并不是每個對象的請求會單獨占用整個連接,多個對象的請求可以通過單個連接發送到服務端。Reply Message中的request_id與對應的Request Message中的request_id的值是一致的。
reply_status用于表示對應請求的完成狀態和Reply Body的內容 ,如何方法請求調用成功完成,那么它的值是NO_EXCEPTION,并且Reply Body中包含返回值。否則Reply Body中的內容將會是:
1、異常值。
2、要求客戶端重發請求到另一個服務端地址上的對象。
3、要求客戶端提供更多用于定位對象的信息。
service_context包含了服務端發送給客戶端的ORB service信息
?Reply Body
和Request Body一樣,在GIOP 1.0和1.1中,Reply Header緊接著就是Reply Body。從GIOP 1.2開始,Reply Body的起始位置需要對齊到8字節。Reply Body的內容由Reply Header中的reply_status的值決定:
如果reply_status的值為NO_EXCEPTION,那么Reply Body首先序列化返回值,然后是IDL方法定義中的out和inout參數(從左到右順序),如下面方法
double example(in short m, out string str, inout long p)那么該方法請求在NO_EXCEPTION的情況下的返回值為:
struct{return double;string str;long p; }
如果reply_status為USER_EXCEPTION或者SYSTEM_EXCEPTION的話,那么reply body包含方法拋出的異常值。
當reply_status為SYSTEM_EXCEPTION,那么reply body的結構如下:
module GIOP { // IDL struct SystemExceptionReplyBody {string exception_id;unsigned long minor_code_value;unsigned long completion_status;}; };minor_code_value的高20位表示廠商Minor Codeset ID(VMCID),低12位表示minor code。一些廠商(可以是多個)可能想定義一些特有的異常MInor code,那么它們應該向OMG去申請這個VMCID。注意OMG標準的VMCID為0x4f4d0('O', 'M')。
如果reply_status的值為LOCATION_FORWARD的話,那么Reply Body包含了一個對象的IOR,然后客戶端ORB負責將原來的請求重新發送到這個IOR指定的對象上,這個過程對于客戶端應用程序是透明的(客戶端并不能知道這些過程)。
如果replay_status的值為LOCATION_FORWARD_PERM,它的行為表現和LOCATION_FORWARD幾乎是一樣的,另外它被服務器用來向客戶端表明 ,它可能要將當前對象的IOR替換成新的,并且新的IOR和舊的還是有效的,但推薦使用新的。
如果reply_status的值為NEED_ADDRESSING_MODE的話,Reply Body包括了一個GIOP::AddressingDisposition,客戶端負責使用request addressing mode(提供更多對象信息)重新發送請求到這個對象上。同樣的,重發過程對于用于程序是透明的,ORB并不會通知客戶端的程序這個過程。
completion_state根據Standard Exception Definitions的IDL定義,它有COMPLETED_YES, COMPLETED_NO, COMPLETED_MAYBE三種狀態。
轉載于:https://www.cnblogs.com/mosmith/p/5196100.html
總結
以上是生活随笔為你收集整理的CORBA GIOP消息格式学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 创建安卓app的30个经验教训
- 下一篇: Solr入门和实践以及我对Solr的8点