Previous Up Next

Chapter 11  On-the-wire Protocols

11.1  GIOP, IIOP and the Protocol Stack

Many people care that a CORBA client can communicate with a CORBA server, but they do not care how the communication happens. However, some people do care about (the principles that underlie) the low-level details of CORBA’s communication infrastructure. There may be several reasons for their interest, for example:

The OMG decided to provide an efficient and flexible inter-application communication mechanism by designing a multi-layer protocol stack, as shown in Figure 11.1. All CORBA on-the-wire protocols are designed around some common guidelines. This makes it relatively easy to build bridges between the different protocols. One of the common guidelines includes the definition of an IOR (Chapter 10), which holds the “contact details” of an object, regardless of which on-the-wire protocol is being used.

Figure 11.1: The hierarchy of CORBA protocols

An Environment-Specific Inter-ORB Protocol (ESIOP) is an on-the-wire protocol that is optimized for a specific environment. For example:

Aside from ESIOPs, all CORBA protocols are based on the General Inter-ORB Protocol (GIOP). This protocol defines the different message types (such as request and reply messages) that can be exchanged between client and server applications and also specifies a binary format for the on-the-wire representation of IDL types (boolean, long, string, enum, struct, sequence, union and so on).

The main thing that GIOP does not specify is the actual networking technology that is used to transmit messages between clients and servers. For example, GIOP does not specify if messages should be transmitted over TCP/IP, X25, ATM or some other transport. Instead, the choice of transport mechanism is decided in a specialization of GIOP. The most well-known GIOP specialization is the Internet Inter-ORB Protocol (IIOP), which is for use on TCP/IP networks. All CORBA products are obliged to support IIOP, but they may optionally support other GIOP-based protocols or ESIOPs too. An IOR contains the contact details for all the protocols that clients can use to communicate with an object in a server.

11.2  Marshaling IDL Types

The act of placing IDL types into a binary buffer (in preparation for transmission) is called marshaling. Conversely, extracting IDL types from a binary buffer is called unmarshaling.1 The marshaling rules used by CORBA are called Common Data Representation (CDR). CDR consists of several rules, as I now discuss.

One CDR rule is that data is marshaled using the native endian format of the computer that is sending a message. A flag in the header of GIOP messages specifies whether the message is in big-endian or little-endian format.2 If the receiving computer uses the same endian format then the data can be unmarshaled directly; otherwise byte-swapping is performed when unmarshaling numeric values. In effect, this CDR rule provides an optimization for the common case where a client and server run on machines with the same endian format.

Another CDR rule states how many bytes of memory basic data-types use and their memory-alignment requirements:

The memory-alignment rules of CDR were chosen to be similar to the memory-alignment requirements of some CPUs. It was hoped that this would facilitate optimizations in CORBA’s transport layer. However, experience has shown that the memory-alignment requirements are mainly a small coding nuisance for people implementing CORBA. Such is the benefit of hindsight.

Finally, there are rules for how compound types are marshaled in terms of the basic types:

The above discussion is not exhaustive, for example, it does not discuss the marshaling of a TypeCode or a valuetype. However, it does provide a representative example of how CDR marshaling works. The memory-alignment rules imply that a GIOP message may contain some padding bytes, which is wasted bandwidth. However, in practice, padding bytes usually account for only a few percent of a GIOP message, so the wasted bandwidth is quite minimal.

11.3  GIOP Message Types

GIOP defines eight different types of messages that can be transmitted between client and server applications. Theoretically, a developer should not need any knowledge of these different types of messages. However, in practice, a knowledge of the different message types can be useful when debugging a CORBA application, particularly if the CORBA vendor product can print low-level diagnostics about the messages that are being sent and received.

The first 4 bytes of every GIOP message contain the letters G-I-O-P. This acts as a “magic number” that identifies the message as being in GIOP format.

The GIOP message types are: Request, Reply, Fragment, CancelRequest, CloseConnection, MessageError, LocateRequest and LocateReply. The following subsections discuss each of these message types.

11.3.1  Request and Reply Messages

As might be expected, Request and Reply messages are used to send requests from a client to a server and replies back from the server to a client, respectively.

11.3.2  LocateRequest and LocateReply Messages

A LocateRequest message is like a “ping” message that asks: “Is the object there?”. The reply is sent back in a LocateReply message. CORBA introduced these messages to make its GIOP redirection mechanism more efficient, as will be discussed in Section 11.4.

11.3.3  Fragment Messages

If a Request or Reply message is very large then the CORBA runtime system might decide to transmit it in several pieces rather than as one monolithic message. In such cases, the first piece is sent as a normal Request or Reply message, but a flag in the header of the message indicates that there are more pieces to follow. The remaining pieces are transmitted as Fragment messages.3

A CORBA product is not obliged to split large messages into smaller fragments, but it has the option of doing so. If a CORBA product is capable of sending Fragment messages then typically a value in a runtime configuration file specifies the maximum size of an unfragmented message. Regardless of whether or not a CORBA product can send fragmented messages, it is obliged to be able to receive fragmented messages.

11.3.4  CancelRequest Messages

A CORBA client may specify a timeout value when making a remote call. If the client does not receive a Reply message before the specified timeout has occurred then the CORBA runtime system in the client gives up waiting for the Reply message and instead throws a TIMEOUT exception back to the client application code. The CORBA runtime system in the client may also (but is not obliged to) send a CancelRequest message to the server, to let the server know that the client will ignore a Reply message if one is later sent.

The CORBA runtime system in the server can use the CancelRequest message as a hint to discard the previously-received Request. However the server can ignore this hint and, in fact, will ignore it if the server has already started dispatching the previously received Request message. This is because the CORBA runtime system in the server cannot know how to “cancel” partially-executed application-level code in the body of an operation.

11.3.5  CloseConnection Messages

CORBA allows idle socket connections to be closed. If a connection from a client to a server is closed then a new connection can be opened transparently if, later on, the client makes more calls to the server. This closing of idle socket connections enables a CORBA server to scale up to deal with thousands, or tens of thousands of interactive clients.

CORBA needs to guard against the following possibility. A client’s socket connection to a server might have been idle for some time and so the server decides to close the socket connection. However, just as the server is about to close the connection, the client sends a request to the server. If the client’s sending of the request overlaps with the server’s closing of the socket then the client might think that the server process had crashed. To prevent this scenario from occurring, a server process sends a CloseConnection message to the client immediately before closing the socket connection. When the client receives this message, it assumes that the server will ignore any requests that the client had recently sent but for which it had not yet received replies. Because of this, the client would (transparently) open a new connection to the server and re-send the requests.

11.3.6  MessageError Messages

If a CORBA application receives a message that is not in GIOP format then it sends back a MessageError message to say “You sent me garbage!” This message should not normally be sent if a CORBA client is communicating with a CORBA server (unless there are serious bugs in the CORBA product(s) being used).

You can force a CORBA server to send a MessageError message by getting a non-CORBA application to send a message to the CORBA server. For example, if you know which port a CORBA server is listening on then you can connect to the server with telnet. When you type something into telnet and hit the ENTER/RETURN key, the non-GIOP message will be sent to the CORBA server, and the CORBA server should send a MessageError message back (and probably close the connection). Within telnet, you will see the letters G-I-O-P followed by some non-printable characters (which is the rest of the very short MessageError message).

11.4  GIOP Redirection

When a client sends a Request message to a server, it receives back a Reply message. The header of the Reply message indicates if it is:

  1. A “normal reply”. In this case the body of the reply contains inout/out parameters and the return value, if any.
  2. An “exception reply”. In this case the body of the reply contains a CORBA exception. When the CORBA runtime system in the client receives such a reply, it unmarshals the exception and re-throws it so that the calling code can catch it.
  3. A“redirection reply” (actually called LOCATION_FORWARD in CORBA terminology). This tells the client that the target object does not live in the server process but rather lives somewhere else. The body of this reply contains an IOR that redirects the client to where the object really resides. The CORBA runtime system in the client then transparently resends its message using the new IOR. Furthermore, the client sends all future requests using the new IOR. In effect, the redirection occurs for only the first request; subsequent requests go directly to the target object.

The redirection reply message is used by CORBA vendors to implement an implementation repository (IMR), which is discussed in Chapter 7. IMRs add a lot of flexibility to CORBA, but there is potentially a significant overhead to be paid for the initial redirection. I now discuss this overhead and an optimization that reduces it. Let us assume that a client’s first request to an object contains a sequence<octet> parameter that is several megabytes large (the data might be a digitized image or a large sound file). When the client receives the redirection reply, it will have to retransmit the multi-megabyte request using the new IOR. Obviously, having to transmit such a large request twice would be a waste of bandwidth. For this reason, CORBA defines the LocateRequest and LocateReply messages (discussed in Section 11.3.2). A LocateRequest message is a very compact “ping”-style message that asks: “Is the object there?”. The reply is sent back in a LocateReply message. The intention is that the CORBA runtime system in a client application will (transparently) send a LocateRequest message before sending the first “real” request. This ensures that if a redirection occurs then the redirection will be dealt with before any (potentially large) “real” requests are transmitted.

Use of LocateRequest messages is an optional optimization. Some CORBA products (especially older ones) never send LocateRequest messages. Some other CORBA products always send a LocateRequest message before the first “real” invocation. It is possible to imagine a CORBA product that is implemented so it normally sends a LocateRequest message but optimizes it away if the first “real” request is quite small. However, it is unlikely that many CORBA products are implemented this way because the performance gain to be made from optimizing away “unnecessary” LocateRequest messages is insignificant in real-world deployments.

11.5  Active Connection Management (ACM)

The CORBA GIOP protocol infrastructure allows idle connections between a client and server to be closed, and transparently re-opened if the client later wishes to send another request to the server. This allows CORBA implementations to optimize their usage of network resources. The CloseConnection GIOP message (discussed in Section 11.3.5) provides the low-level infrastructure required for this. The CORBA standard does not define any terminology for this ability to close idle connections. Because of this lack of CORBA-provided terminology, some vendors have defined their own terminology for this capability. Both Orbix and Orbacus use the term active connection management (ACM) to refer to the automatic closing of idle socket connections. Other CORBA products might use different terminology for the same concept.

CORBA does not require that a product implement ACM; rather, it is an optional capability, though good-quality, modern CORBA products should implement it. Neither does CORBA specify what heuristics should be used to close idle connections. For example, in Orbacus, entries in a configuration file are used to specify that connections should be closed if they have been idle for a specified amount of time. Orbix uses a different mechanism: configuration entries specify the maximum number of open connections; once this limit has been reached, Orbix closes the connection that has been idle for the longest period of time. Other CORBA products may employ different heuristics for closing idle connections.

11.6  Service Contexts

The concept of a service context is easy to understand. However, the terminology is not very intuitive. The context part of the name arises because a service context is used to pass extra “contextual” information with request and reply messages. The service part of the name arises because the first uses of service contexts were to help implement some of the CORBA Services, such as the Security Service and Object Transaction Service (OTS). However, documented APIs mean that service contexts can also be used by application programmers.


module IOP { … typedef unsigned long ContextId; struct ServiceContext { ContextId id; sequence<octet> data; }; … };
Figure 11.2: IDL definition of a service context

The IDL definition of a service context is shown in Figure 11.2. As can be seen, a service context is simply a struct that contains binary data and an integer value. The integer value is an identifier that specifies how the binary data should be interpreted. For example, one integer value specifies that the binary data contains information used by OTS; another integer value specifies that the binary data contains information used for security; and so on. Organizations can contact the OMG to request unique integer values that they can use for service contexts specific to their own needs.

The header of each Request and Reply message contains a sequence of service contexts. Unless an application makes use of, say, OTS, it is likely that most messages sent and received by the application will contain an empty sequence of service contexts. An application can use a portable interceptor (Chapter 14) to add a service context to outgoing messages and can interrogate incoming messages to see if they contain a service context that corresponds to a specified integer identifier. In this way, if an application receives a service context that it is not expecting then the service context is simply ignored.

11.7  Codeset Negotiation

Codeset is an abbreviation of coded character set. Examples of codesets include ASCII and Unicode. In the early versions of CORBA, ISO Latin 1 (which is US ASCII extended with accented characters) was the hard-coded codeset used to transmit parameters of type char or string. However, CORBA has matured to support wide characters (IDL types wchar and wstring).4 CORBA has also matured so that the codesets used to transmit characters and wide characters are no longer hard-coded, but rather are negotiated when a client application establishes a connection to a server. As might be expected, this is called codeset negotiation. Codeset negotiation is achieved through the following steps:

  1. An object reference (Chapter 10) exported from a server contains “contact details” (for example, host, port and object key), but also contains details of which codesets can be used to communicate with the object. In particular, the object reference specifies the native char and wchar codesets used by the server application plus a sequence of codesets that can be used to transmit char or string types, and another sequence of codesets that can be used to transmit wchar or wstring types. 5 The codesets in the sequences are called conversion codesets because they are codesets for which the CORBA runtime system in the server application can convert to and from its native codeset.
  2. When a client application imports an object reference, it compares the codesets in the object reference to the codesets that its own CORBA runtime understands. If the CORBA runtime in the client cannot find any overlap between its own codesets and those of the server then it throws an exception to indicate that it cannot communicate with the object. Assuming that it can find codesets common to both itself and the server, the CORBA runtime system in the client decides which codesets it will use for communication.
  3. When a client sends its first Request message to the server, it uses a service context (Section 11.6) to tell the server which codesets it has chosen for communication. These are called the transmission codesets.

11.8  Bidirectional GIOP/IIOP

The specification of GIOP/IIOP states that it is a unidirectional protocol. A client transmits a message on a channel (GIOP terminology for what is known as a socket in IIOP) to a server and the server transmits its reply on the same channel. This sounds like bidirectional communication since messages are transmitted in both directions. However, the reason why it is said to be unidirectional is that request messages can be transmitted in only one direction. GIOP does not allow a server to transmit a request to a client on the same channel that the client uses to transmit requests to the server. This is illustrated in Figure 11.3. The client opens a channel to a server in order to invoke upon obj1, and the client passes a reference to a callback object (obj2) as a parameter to an invocation.6 Later, if the server wants to invoke an operation on this callback object then GIOP does not allow the server to send its request on the already-open channel. Instead, the server must open a new channel to the client.

Figure 11.3: Channel usage in (unidirectional) GIOP
Figure 11.4: Channel usage in bidirectional GIOP

When GIOP was being defined, there was some debate within the OMG over whether GIOP should be a unidirectional or bidirectional protocol. However, it was decided that GIOP should be a unidirectional protocol and almost everybody has regretted this decision ever since. In particular, unidirectional protocols have two drawbacks when callback objects are used:

  1. Opening a second channel is wasteful of network resources.
  2. In some real-world deployments, there may be some firewalls between the client and server applications. Although it may be possible for the client to successfully traverse firewalls to establish a connection to a server, it is sometimes impossible to establish a new connection from the server back to the client.

To work around these problems, a bidirectional enhancement to the GIOP/IIOP specification (illustrated in Figure 11.4) was added to CORBA 2.4.


1
Marshaling and unmarshaling are commonly used terms. However, some people use other terms, such as encoding and decoding, or serializing and deserializing.
2
Big-endian and little-endian refer to the whether multi-byte integers are stored in memory with their most-significant or least-significant byte first. These terms originate from the book Gulliver’s Travels written by Jonathan Swift in 1726. In this satirical novel, a race of people have a civil war because they cannot agree if a hard-boiled egg should be eaten starting at its big end or its little end.
3
Support for fragmentation of Request and Reply messages was introduced in GIOP 1.1. In addition, support for fragmentation of LocateRequest and LocateReply messages was added in GIOP 1.2.
4
A codeset is said to be wide if every character is represented by a 16-bit or 32-bit fixed-length number.
5
To be pedantic, an object reference contains integers that uniquely identify codesets rather than the codesets themselves.
6
Callback objects are discussed in Section 1.4.2.2.

Previous Up Next