Behind the Scenes - Client Side Message Flow (Delphi)

In this article we're going to take an in-depth look at what goes on behind the scenes as Remoting SDK processes your client side call, dispatches it to the server and returns the server's response as result of your method call.

The above diagram shows a call from the client to the server: your user code calls your method - say - Sum(1,2) - on the local Proxy. The Proxy object sends this call across the network to the server, where the Invoker receives it and calls your server's implementation of the Sum() method. The same happens on the way back, and the Proxy returns control to your user code with the result.

The above view is probably all you need if you want to use Remoting SDK in your applications, but if you want to extend RO with your own components, or simply would like to get a deeper understanding of what's going on in detail, we'll need to look behind the scenes a bit. So let's extend the diagram with a few more details and look at them more closely (click on the diagram for a close-up).

In this diagram, we see several new objects introduced that help getting the message call across the net and executed, and getting the results back. Let's look at these in some more detail:

The Message

You're probably familiar with the Message classes, because you are used to drop them on your client and server form when you create a new application. But what does a Message do?

A Message object is used to represent the individual information of your method call, such as the name of the service, the name of the method you are calling, as well as all the parameters that you need to send to the server. Different Message classes can be used to express this information in different ways, and, as you probably know, Remoting SDK comes with several message types: BinMessage, JsonMessage, PostMessage, SOAPMessage and XMLRPCMessage.

So the first question is, how is this information written to the Message object? If you take a look at the Proxy code that is generated for your RO applications (for each Service you define, RO will generate a Proxy class inside your libraries _Intf.pas file), you will see something like this:

function TNewService_Proxy.Sum(const A: Integer; const B: Integer): Integer;
var
  lMessage: IROMessage;
  lTransportChannel: IROTransportChannel;
  lResult: Integer;
begin
  lMessage := __GetMessage();
  lMessage.SetAutoGeneratedNamespaces(DefaultNamespaces());
  lTransportChannel := __TransportChannel;
  try
    lMessage.InitializeRequestMessage(lTransportChannel, 'NewLibrary', __InterfaceName, 'Sum');
    lMessage.Write('A', System.TypeInfo(Integer), A, []);
    lMessage.Write('B', System.TypeInfo(Integer), B, []);
    lMessage.Finalize();
    ...

Because the CodeGen that creates this unit has intimate knowledge of the parameters of each function, it can create Delphi code that writes each parameter to the message. First, the message object is initialized with Library Name, Interface/Service Name and Method Name of the function you're going to call. Next, the message's Write() method is called for each incoming (const or var) parameter, and lastly the message is finalized. At this point, the Message object holds a message that is ready to be written to a stream and sent over the network. What exactly this message looks like will of course depend on the individual message class that's currently in use.

    lTransportChannel.Dispatch(lMessage);

Following along the code in the proxy, we see that our message content is now being written to a stream, and then passed on to the Transport Channel. Eventually we'll get a response back and processing will continue, but let's not get ahead of ourselves.

The Transport Channel

Just like with the Message classes, you're probably aware of the Transport Channels, because RO installs a variety of them in the Component Palette. Most of these channels map one-on-one directly to a specific Server class (also found in the Component Palette), but some of them are more generic and can work with several different servers (such as HTTP or TCP ones).

The job of the Transport channel is really simple: the call to Dispatch() will directly forward the passed streams to IntDispatch, which is a virtual abstract method in TROTransportChannel and is overridden in descendant classes to implement the actual logic. Depending on the type of channel you are using, this method might build up a network connection to the server, send an email or use some local means of communication like a Windows message or a Named Pipe to talk to the server. Once the channel is talking to the server, it will transmit the Request stream to the server for processing and eventually get a Response stream back.

Diagrams created with Enterprise Architect by Sparx Systems