Computer Systems
Remote Procedure Calls


Many modern applications use a distributed computing architecture in which a user must access data or service across a network. Client/server is a computational architecture that involves client processes requesting service from server processes. Client processes usually manage the user-interface portion of the application, validate data entered by the user, and dispatch requests to server processes. The client process is the front-end of the application that the user sees and interacts with. The server process fulfills the client request by performing the task requested and passing back the results. The server process may run on a different machine on the network. In theory, the server could be written for Linux and the client could be written for Win32, however the reality is somewhat more complicated.

Remote Procedure Call (RPC) defines a powerful technology for creating distributed client/server programs. The idea behind RPC is conceptually very simple: make distributed computing look like a function call, at least to the calling process. This enables you to focus on the details of the application rather than the details of the network communication. The calling process behaves as it usually does when making a function call:

When the calling process calls a function, the action performed by that function will not be the actual code as written, but code that begins network communication. It has to connect to the remote machine, send all the parameters, wait for replies, unpack the results and return. This is the client side stub. The server side stub on the remote machine waits for messages asking for a function to run, reads the parameters, passes them to the local function, then send the results back to the calling process after execution.

To summarize, the RPC usage paradigm is:

Client stub:

  1. Pack the request and the arguments into a network message. This is called marshalling
  2. Send the message to the remote server
  3. Block until the results arrive
  4. Unmarshall the results, place them on the stack for the local process
  5. Resume execution

Server stub:

  1. Receive a function request
  2. Unmarshall the arguments from the network message
  3. Invoke the function call locally
  4. Marshall the return results into a network message
  5. Send the message back to the client process
The client and server programs will be compiled separately. The communication protocol is achieved by stubs generated with the rpcgen compiler. This is a powerful compiler does most of the dirty work, so that programmers can focus on the main features of their application, rather than spending time debugging their network interface code. The output of rpcgen is:


Here is a simple example that creates a remote Greatest Common Divisor server using a Unix tool called rpcgen (RPC generate).
  1. Log in to tanner. In your systems directory, create another directory called rpc, then change your current directory to rpc.

  2. Write the normal, "local functions only" version of your program first. The only real modification here is that in anticipation of using RPC, you must limit your functions to accepting a single argument. This is the sample code for the gcd program (gcd.c).

  3. Create a specification file (it must have the .x extension). This is used to define the RPC functions that will be executed remotely and any special data types. Functions are restricted: they may take at most one in parameter, and return at most one out parameter as the function result. If you want to use multiple parameters, you have to wrap them in a single structure, and similarly with the return values. Multiple RPC functions (numbered from one upward) may be defined at once. Each RPC function is uniquely identified by a program number, version number, and function number. The first implementation of a program will most likely have version number 1 (and incremented when functionality is changed in the remote program). RPC programs should be assigned program numbers according to rules detailed in "Program number assignment".

    This is a sample specification file (gcd.x).

  4. Run rpcgen on your specifcation file to generate the code templates.
        rpcgen -a gcd.x 
    The "-a" option stands for "all", which tells rpcgen to generate all the files, including sample code for client and server side. This will generate the files:

  5. Edit the server skeleton (follow the suggestions in gcd_server.c) to include the body of the function to be executed remotely. The key here is paying attention to argument and function return type.

    It is essential to have result (in this example) declared as static; otherwise, if result were declared local to the remote procedure, references to its address would be invalid after the remote procedure returned.

  6. Edit the client skeleton (follow the suggestions in gcd_client.c) to combine the functionality of the original program with the remote access functionality. There are five things to note here:

  7. Compile the client code
         gcc gcd_client.c gcd_clnt.c gcd_xdr.c -lnsl -o xclient
    Here the option -lnsl stands for "link the network socket library," necessary to handle the communication over the network.

  8. Compile the server code
         gcc gcd_server.c gcd_svc.c gcd_xdr.c -lnsl -o xserver
    Once created, the server should be copied to a remote machine and run. (If the machines are homogeneous, the server can be copied as a binary. Otherwise, the source files will need to be copied to and compiled on the remote machine.)

  9. Startup the server: on the remote machine, type in
        xserver & 

  10. Test the server with the client: on the local machine, type in
        xclient remoteHostName 
    Here remoteHostName is the name of the remote machine running the server, for example

  11. Repeat the steps above (starting with step 2) to add two more functions Add and Subtract to your server, with the same argument type as computeGCD, and invoke them in your client code. Recompile and test your client and server.