Industry Publications Index ... Click Here




The RPC Programming Interface

Originally published  July, 1996
by Carlo Kopp
1996, 2005 Carlo Kopp

In September 1995 issue of OSR we discussed NFS and the ONC RPC protocol, with an emphasis on NFS. In this issue we return to take a closer look at the RPC model and its programming interface.

Introduction

The Open Network Computing Remote Procedure Call protocol, or ONC RPC, has proven to be the most durable of the early generation of distributed computing products. Indeed, enmeshed with the NFS protocol it is part of a standard Unix system's potent arsenal of networking tools.

Whilst Sun no doubt had grander objectives for RPC than it becoming a lower layer of a mainstream file serving protocol, the realities of a fragmented marketplace, and a technologically timid customer base conspired such that RPC never attained the ubiquity which Sun had no doubt intended for it. Nevertheless, the availability of the RPC programming interface to the programmer provides those prepared to play with it, a quite potent interface. Where the programmer does not need the C++ oriented object instantiation and destruction facilities of products like CORBA, RPC may well prove to be perfectly adequate.

Functionally RPC protocols are built around the idea of out-of-process procedure calls. The distinction between in-process and out-of-process procedure calling is that in the former the thread of execution is wholly contained within the process in question, in the latter at some point the thread blocks while a procedure call is executed by another party, for instance a process on a remote host, or the local host (Fig.1).

What an RPC protocol provides is a mechanism whereby a process can execute procedure calls both locally, and in other processes, termed servers. The initiating (calling) process is by convention termed a client process.

The plumbing of the ONC RPC protocol was discussed in some detail in the September, 1995, issue. For the purpose of completeness, it is necessary to state that the RPC protocol must operate in conjunction with a lower level transport protocol (usually User Datagram Protocol or UDP) and a data presentation protocol, in this instance the eXternal Data Representation (XDR) protocol. The XDR protocol is required to ensure that big-endian hosts can talk to little-endianhosts and vice versa. Other aspects of the protocol, such as call specific parameters, are essential to allowing the client to receive appropriate replies from the server (Fig.2).

The programmer seeking to develop an RPC application today has a much richer set of alternatives than a decade ago. The OSF/DCE RPC is increasingly popular, and offers facilities such as transport independence (the ability to run over protocols other than UDP/IP), Threads integration and a more sophisticated programming interface than the original ONC product. The DCE suite is an evolved derivative of the earlier Apollo (HP) NCS product.

Other than these mainstream tools offered by major vendors, a quick browse on the Net will also uncover quite a few proprietary and public domain RPC products, each of which offer one or another "special" feature.

To illustrate the programming model the "classic" ONC RPC is more than adequate, indeed the absence of features "noise" makes it in many respects better suited for a tutorial.







The Programming Interface

Ideally the programming interface to an RPC scheme should be transparent to the programmer, who should be able to call remote procedures as easily as calling local procedures. In practice, however, this is seldom the case.

From a technical perspective, the typical resolution to the dilemma of being able to hide the plumbing, vs the alternative of a programmer becoming wholly immersed in the plumbing, is that of a compromise.

The complexity of the plumbing required to support an RPC scheme should not be underestimated. The programming interface must provide the following facilities:

  • Function calls to the XDR layer to convert argument/return values to/from the types used in the language and the canonical XDR (network) forms.

  • Function calls to the RPC protocol engine to produce the RPC request message, send it, wait for a response, decode the response and forward it back to the XDR layer.

  • A programming interface to the XDR layer and the RPC protocol engine. The latter is required for some state management functions.

A programmer wishing to use an RPC scheme would therefore typically require the insight of an experienced systems programmer should he/she wish to program the low level interfaces.

Sun's resolution to this problem was a very clever idea at the time, and one which has been emulated since by other protocols. This scheme is that of an interface definition language (the current buzzword) and an associated compiler.

This scheme provides a mechanism whereby the RPC interface layer can be inserted between the programming interface and the body of the procedure, in a fashion which is relatively transparent to the programmer.

With ONC RPC the compiler is known as rpcgen. A programmer wishing to produce a remote procedure, or convert a local procedure to be used as a remote procedure, must first produce an interface definition in the interface language, which is termed RPC Language with ONC RPC.

Compiling the interface definition with rpcgen produces the stubs required for the client and server to access the RPC library routines, a skeleton routine for the server end of the RPC program, and XDR conversion routines used to map arguments and return values.

Figure 3. illustrates how this fits together. The client program will be linked with the client side RPC stubs and the RPC library, and the server side skeleton, once fleshed out with the "meat" in the procedure, is compiled and linked with the server side stubs and RPC library code. The stubs provide a mechanism which hides most of the RPC interface, in either direction, from the programmer.

Whilst those experienced with RPC will probably chuckle at this simplification, it is essential to illustrating the fundamental model used. Figure 4 shows the necessary steps in the production of an RPC client server pair.

The RPC and XDR Languages

The RPC Language (RPCL) is the primary tool which must be mastered by the aspiring RPC programmer. A superset of the XDR definition language, RPCL has been described as a C-like language, but it does have some important differences. A good starting point is to look at the data types supported. Due the nature of the beast, some of these differ from their established C-language counterparts.

  • Integers - which are 32-bit long in two's complement notation. Integers are declared as integer .

  • Unsigned Integers - which are 32-bit non-negative numbers. Unsigned integers are declared as unsigned integer.

  • Enumerations - appear as signed integers. An example declaration is enum { APPLE=1, ORANGE=2, LEMON=3 } fruit .

  • Boolean - which are equivalent to the enumeration enum { FALSE=1,TRUE=1} identifier, are declared as bool identifier .

  • Hyper-integers - are 64-bit two's complement longwords, and are declared as hyper integer .

  • Unsigned Hyper-integers - are 64-bit unsigned longwords, and are declared as unsigned hyper integer .

  • Floating point - using the normalised IEEE format in 32 bits. They are declared as float .

  • Double Precision floating point - normalised IEEE format in 64 bits. They are declared as double float .

  • Fixed Length Opaque Data - a type specific to RPC/XDR, opaque data is passed through without being operated upon. It is declared as opaque identifier[n] and may be a fixed size array, padded to multiples of four bytes.

  • Variable Length Opaque Data - allows opaque data buffers of varying size to be transfered, providing that they are smaller than the size declared. The type declaration is opaque identifier, where n is the largest size allowed.

  • Strings - are declared and stored differently from C, the encoding including an integer byte count for the string. An example declaration is string filename<255>, where 255 is the upper bound on string size.

  • Fixed Length Arrays - are declared as type-name identifier[n], where n is array size.

  • Variable Length Arrays - are declared as type-name identifier, where n is the upper bound on array size.

  • Discriminated Union - this type differs from the C declaration by the inclusion of an integer discriminator, used to select the "arm" of the union. The declaration is: union switch (discriminant-declaration){ case discriminant-value-A: arm-declaration-A; case discriminant-value-B: arm-declaration-B; .... default: default-declaration; } identifier;

  • Void - the declaration of voids is used typically where no data is input or output. Voids are declared as void.

  • Constants and Typedefs - both are declared as in C, as const name-identifier=n; and typedef declaration .

The observant reader will have noticed that unlike C, these declarations typically include a sizing component or a bound on size. This is a side effect of having to transmit data in fixed sizes per call. Types such as strings which may have variable sizes in a local procedure must have a fixed size in an RPC environment as appropriate storage must be committed. The option of dynamically adjusting to type size during execution is not a practical proposition.

The type declarations described are the core of the XDR language specification. To create the RPCL, we must add the declarations for a program, a version and procedure. These are declared as: program identifier { version ver-identifier } type-specifier identifier (type-specifier) = constant; } = constant ; } = constant ;

An example being: program BLOGGS { version BLGSVRS { boolean TESTBLOGGS ( input ) = 1; } = 1; } = 0x20000001; This is all it takes to define the syntax for the RPC programming interface (the SMI spec was used as the primary reference).

The XDR model is supported by useful library of primitives, which allow filtering of variables between their respective C language and XDR types. Most are implemented as macros.

Running the rpcgen compiler on the program declaration results in the production of C language source files, as previously noted, which define the stubs to be used at the server and the client ends, and the skeleton function for the server.

The real power of this model, adopted in concept for the C++ oriented CORBA, is that it greatly simplifies the mechanics of producing clean and bug free hooks into the RPC/XDR plumbing. While this may not be an issue with small programs, larger and more complex designs can fall foul of even small errors in the interfaces.

In the next issue we will look at OMG CORBA and how it extends this paradigm.



$Revision: 1.1 $
Last Updated: Sun Apr 24 11:22:45 GMT 2005
Artwork and text 2005 Carlo Kopp


Industry Publications Index ... Click Here