Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

member c is of type char, it can be aligned anywhere, so the value of c is encoded immediately following the string at byte offset 10. The d member of the structure must be aligned on an 8-byte boundary, so c is followed by 5 bytes of padding, followed by the 8 bytes required to hold the value of d.

Note that the size of the structure CD in Figure 13.2 is 13 bytes, whereas in Figure 13.1, a value of the same type consumes 16 bytes. In other words, the amount of padding for a structure varies depending on the starting offset of the structure within a byte stream. This is different from the binary representation of structures in most programming languages. For example, in C++ (at least on most architectures) a structure of type CD would always be aligned on an 8-byte boundary and would consume 16 bytes of memory regardless of what data preceded or followed it. In general, CDR alignment rules apply only to primitive types; there are no separate alignment rules for structured data. Instead, structured data is aligned according to the rules for primitive members, with padding bytes (of undefined value) inserted to maintain alignment.

This example also shows that to correctly decode a CDR-encoded byte stream, the receiver must know what data to expect in advance. For example, the receiver of the byte stream in Figure 13.2 must know in advance that the first data item is a string because that in turns allows the receiver to determine at what offset in the byte stream it can find the structure that follows the string.

Summary

We do not show the encoding of other IDL types here. There are CDR encoding rules that cover all possible IDL types, such as unions, sequences, arrays, exceptions, type codes, type any, object references, and so on. The main point to remember is that all IDL types have well-defined encodings, and that ensures interoperability between ORBs. In general, CDR encoding requires advance knowledge by the receiver of what types of values to expect. This means that CDR-encoded data is not self-describing and that sender and receiver are obliged to honor the interface contract established by IDL definitions.

13.4 GIOP Message Formats

GIOP was first defined by CORBA 2.0, revised with CORBA 2.1, and revised again with CORBA 2.3. This resulted in three versions of GIOP: versions 1.0, 1.1, and 1.2. The main additions in the later versions are support for message fragmentation in GIOP 1.1 and support for bidirectional communication in GIOP 1.2.

Message fragmentation allows for more efficient marshaling of data onto the wire. It permits the sender to send data for a single request in several fragments without having to buffer and marshal in advance all the data for a request.

Bidirectional communication is important for communication through firewalls. For example, the Callback pattern (see Section 20.3) requires a server to also act as a

533

IT-SC book: Advanced CORBA® Programming with C++

client. GIOP 1.2 allows the server to initiate requests on the connection that was opened by the client. This means that the server does not have to open a separate connection for a callback, only to find itself blocked by a firewall.

Later versions of GIOP are backward-compatible with earlier versions. This permits older clients to communicate with newer servers because newer servers must support all previous protocol versions. Similarly, newer clients can communicate with older servers because clients are not allowed to use a later version than the one supported by the server. We do not cover GIOP in full detail in this book. Instead, we cover only a subset to illustrate the general principles. In addition, the discussion that follows covers GIOP versions 1.0 and 1.1. We briefly return to GIOP 1.2 in Section 13.9.

 

Table 13.2. GIOP message types.

Message Type

 

Originator

Request

 

Client

Reply

 

Server

CancelRequest

 

Client

LocateRequest

 

Client

LocateReply

 

Server

CloseConnection

 

Server[a]

MessageError

 

Client or Server

Fragment[b]

 

Client or Server

[a]Can be sent by client or server in GIOP 1.2.

[b]GIOP 1.1 and 1.2.

GIOP has eight message types, as shown in Table 13.2. Of these message types, Request and Reply are the workhorses because they implement the basic RPC mechanism. We show these two message types in some detail and only briefly describe the remainder.

A Request message is always sent from client to server and is used to invoke an operation or to read or write an attribute. Request messages carry all in and inout parameters that are required to invoke an operation.

A Reply message is always sent from server to client, and only in response to a previous request. It contains the results of an operation invocation—that is, any return value, inout parameters, and out parameters. If an operation raises an exception, the Reply message contains the exception that was raised.

By definition, the client is the party that opens a connection, and the server is the party that accepts the connection. To invoke an operation on an object, the client opens a connection and sends a Request message. The client then waits for a Reply message from the server on that connection.

534

IT-SC book: Advanced CORBA® Programming with C++

If client and server must reverse roles—for example, because the server must invoke a callback operation on an object in the client—the server cannot send a request on the connection it accepted from the client. Instead, the server must open a separate connection for which it acts as the client. This means that GIOP is unidirectional as far as client and server roles are concerned.[3]

[3] With GIOP 1.2, client and server can reverse roles while using a single connection. This is particularly important for callback objects provided by applets because the Java sandbox prevents opening of a separate connection to an applet.

To transmit a GIOP message over the wire, the sending side sends a message header, followed by a message body (the contents of the message body depend on the exact message indicated by the header). Figure 13.3 shows the basic structure of a GIOP message.The message header is described in pseudo-IDL:

Figure 13.3 Basic structure of a GIOP message.

module GIOP {

// PIDL

struct Version {

 

octet

major;

 

octet

minor;

 

};

 

 

enum MsgType_1_1 {

Request, Reply, CancelRequest, LocateRequest, LocateReply, CloseConnection, MessageError, Fragment

};

 

 

struct MessageHeader_1_1 {

// The string "GIOP"

char

magic[4];

Version

GIOP_version;

octet

flags;

 

octet

message_type;

 

unsigned long message_size;

}; // ...

};

We show the GIOP 1.1 header here (the 1.0 header is very similar). A message header consists of 12 bytes and precedes every GIOP message. Figure 13.4 shows a graphical representation of the components of a message header. The layout of a message header is as follows.

Figure 13.4 A GIOP 1.1 message header indicating a Request message in big-endian byte ordering and without fragmentation.

535

IT-SC book: Advanced CORBA® Programming with C++

The first 4 bytes of a message header are always the characters GIOP. These characters indicate that the message is a GIOP message and also serve to delineate message boundaries.

Bytes 4 and 5 are the major and minor version numbers as 8-bit binary values. Figure 13.4 shows a GIOP 1.1 header; both major and minor version numbers are 1.

Byte 6 is a flags byte. The least significant bit of the flags byte indicates whether the remainder of the message is in big-endian or little-endian encoding: a value of 0 indicates big-endian. The second-least significant bit indicates fragmentation. A value of 1 indicates that this message is a fragment with more fragments to follow. A value of 0 indicates that this message is a complete message or is the last message in a sequence of fragments.

Byte 7 indicates the message type. Its value is the ordinal value of one of the MsgType_1_1 enumerators. The value 0 indicates a Request message.

Bytes 8-11 are a 4-byte unsigned value that indicates the size of the message (not counting the 12 header bytes). The value is encoded as big-endian or little-endian as indicated by the least significant bit of the flags byte.

13.4.1 Request Message Format

A Request message consists of three parts, as shown in Figure 13.5. Following the GIOP header, a Request message contains a Request header and a Request body. The Request header and Request body together form the GIOP message body. The Request header has the following definition:

Figure 13.5 A GIOP Request message.

module GIOP {

// PIDL

// ...

 

struct RequestHeader_1_1 { IOP::ServiceContextList service_context;

unsigned long

request_id;

boolean

response_expected;

octet

reserved[3];

sequence<octet>

object_key;

string

operation;

Principal

requesting_principal;

};

 

// ...

 

};

 

536

IT-SC book: Advanced CORBA® Programming with C++

The fields of the Request header are as follows.

service_context

This sequence contains service data that is silently added to each request by the ORB run time. Its main use is to propagate information required by some ORB services, such as a transaction identifier if the request is made as part of a transaction, or a security context for ORBs that implement the OMG Security Service.

request_id

This field is used by the client to associate the request with its response. The client sets the request_id to a unique number when it sends the request. A Reply message also has a request_id field; when the server sends the reply for a request, it returns the corresponding request_id to the client. In that way, the client can have replies for more than one request outstanding at a time.

response_expected

This field is a Boolean value that is set to true for a normal synchronous request, meaning that the client requires a reply for the request. If the operation being invoked by the client is a oneway operation, the client-side run time can set this field to false (to indicate to the server that no reply is wanted) or to true to allow the client to receive a system exception or a LOCATION_FORWARD reply (see Section 13.4.2).

reserved

These three bytes are reserved for future use and are always set to zero for GIOP 1.1. object_key

The object_key field is the object key of the IOR that was used to invoke the request (see Section 2.5.3). It identifies the particular object in the server that the request is for.

operation

This field is a string that contains the name of the operation being invoked. If the client sends the request to read or write an attribute, the operation name is

_get_attribute_name or _set_attribute_name, respectively.

For operations on the Object base interface, the operation names are _interface,

_is_a, and _non_existent. They correspond to the get_interface, is_a, and non_existent operations on Object. Note that there are no operation names defined for the other operations on Object—namely, duplicate, release, is_nil, is_equivalent, and hash. These operations are always processed by the local ORB and never result in a remote message.

requesting_principal

This field indicates the identity of the calling client for use with the BOA. It is now deprecated because the OMG Security Service instead uses the service_context to indicate the identity of the caller.

537

IT-SC book: Advanced CORBA® Programming with C++

The important fields of a Request header are the operation name, which identifies the operation or attribute, and the object key, which identifies the target object. The remaining data for the request are part of the Request body.

The Request body, which immediately follows the variable-length Request header[3] , contains the in and inout parameters for the request, optionally followed by a Context pseudo-object. (A Context object is present only if the operation definition has a Context clause—see Section 4.13.) The in and inout parameters are marshaled as if they were members of a structure containing the leftmost in or inout parameter to the rightmost in or inout parameter. For example, consider the following operation:

[3] GIOP 1.2 aligns the Request body on an 8-byte boundary instead.

interface foo { void op(

in string param1, out double param2, inout octet param3

);

};

The parameters are sent as if they were part of the following structure:

struct params { string param1; octet param3;

};

Parameter values for the request are sent as if this structure were encoded according to CDR encoding rules. Note that the structure does not contain a member for param2. This parameter is missing because it is an out parameter; there is no point in sending an out parameter from client to server.

13.4.2 Reply Message Format

A server sends a Reply message in response to a client's Request message provided that the response_expected flag of the request was set to true. Like a Request message, a Reply message consists of three parts, as shown in Figure 13.6.

Figure 13.6 A GIOP Reply message.

538

IT-SC book: Advanced CORBA® Programming with C++

Following the GIOP header, a Reply message contains a Reply header and a Reply body that immediately follows the header.[4] The Reply header and Reply body together form the GIOP message body. The Reply header has the following definition:

[4] GIOP 1.2 aligns the Reply body on an 8-byte boundary instead.

module GIOP { // PIDL // ...

enum ReplyStatusType { NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION, LOCATION_FORWARD

};

struct ReplyHeader { IOP::ServiceContextList service_context; unsigned long request_id; ReplyStatusType reply_status;

}; // ...

};

The fields of the ReplyHeader are as follows.

service_context

As with a Request header, this field is used to transparently propagate implicit context information required by ORB services such as the Security and Transaction Services.

request_id

The request_id field returns the ID of the corresponding request to the client. The client uses it to associate replies with requests. This allows the client to have several replies outstanding simultaneously. The server need not send replies in the same order in which it receives requests because some requests may take longer to complete than others.

reply_status

The reply_status field indicates the result of the request.

NO_EXCEPTION

This indicates that the request completed successfully.

USER_EXCEPTION

The request raised a user exception.

SYSTEM_EXCEPTION

The server-side ORB or the server-side application code raised a system exception.

LOCATION_FORWARD

539

IT-SC book: Advanced CORBA® Programming with C++

This reply indicates that the request cannot be processed by this server, but the client should try again at a different address. We discuss the use of this message in Section 14.4.5.

The reply_status field also determines how the Reply body is interpreted by the client. If the operation completed successfully, the Reply body contains the return value, followed by all out and inout parameters for the operation. As with a Request body, the return value and parameters are encoded as if they were members of a structure. If reply_status indicates a user exception, the Reply body contains the repository ID of the exception, followed by the data members of the exception. If the request raised a system exception, the Reply body contains the repository ID of the system exception and its minor code and completion_status. If reply_status is

LOCATION_FORWARD, the Reply body contains an object reference that the client can use to retry the request.

13.4.3 Other Message Formats

The remaining six message formats either are control messages or are provided to permit optimizations. Because they are not relevant to the basic remote procedure call mechanism, we touch on them here only briefly (see [18] for more information).

CancelRequest

With this request, a client can inform a server that it has lost interest in the results of an operation. A client can use this request if, for example, a user cancels a long-running operation. Note that a CancelRequest never aborts an operation implementation while it is executing. Instead, it simply informs the server that it need not bother to send any reply when the operation has completed.

LocateRequest

Clients can use this request to get the current addressing information for an object. The LocateRequest message and the corresponding LocateReply message can reduce the overhead of locating an object (see Section 14.4.6 on page 644).

LocateReply

This is the reply sent by a server in response to LocateRequest message.

CloseConnection

A CloseConnection message from a server informs the client that the server is about to close the connection. If the client wants to communicate with the server again later, it must open a new connection to the server. Typically, a server sends this message if too many clients are connected and the server is about to reach its incoming connection limit.

The CloseConnection message is required because without it, clients could not distinguish intentional shutdown from disorderly shutdown: if the server were to simply

540