What is RPC?

In computer programming a “procedure” is a set of instructions packaged into one unit. For instance: say you regularly need to open a text file, scan it for certain text, and output a yes or no depending on whether there’s a match. One way is to keep writing these instructions at each place in your program. Another way would be to put these instructions at one place in your program, give that logical grouping a name, and then on every time you need to run these instructions simply invoke that grouping. The latter is an easier approach, not only because it saves your typing (or copy-pasting) the same code all over the place, but also because if you ever need to improve the instructions you just have to do it at one place. This logical grouping is what’s known as a procedure. Other names for a procedure are function, subroutine, and method.

When a procedure is invoked at a point in the code, usually with any inputs that are to be passed on to the procedure, the inputs are saved someplace and the procedure is loaded and executed. The main program is on hold while the procedure runs, its current state is stored at a location and execution moves to the procedure. The procedure reads the inputs from the saved place, does its deeds, stores any outputs in a designated place, and passes execution back to the main program (whose location was previously stored). The main program then reads the output and continues.

To give an analogy: think of it as though you are reading a book and you find a word whose meaning you do not know, so you keep down the book and open a dictionary to find the meaning. “Reading a book” is the main program; the “looking up the meaning” is a procedure that takes as input a word whose meaning you want, scans a dictionary, and gives as output an understanding of the word. The word itself is stored in your memory, and so is the the output (the meaning). That’s pretty much how procedures work.

Procedures can be part of the code of the main program or separate from it. They can even be from a 3rd party, packaged into files (such as DLLs) containing multiple procedures. When writing code the author links to these files and invokes procedures from them. When compiling the code to turn them into machine instructions the compiler puts in the required details on how to find the procedures (their address and such). That’s how the processor, which actually executes the program, knows where to find the procedure and what it expects.

When executing a program the processor only has access to its memory space. And since a procedure is linked to the program and has to be executed by the processor, it too must be in the same memory space as the main program. So procedures have to be local to the main program, i.e. on the same computer as the program. They can’t be on a remote computer. If a procedure is on a different machine (a different memory space) the processor has has no way of connecting to it.

As computers started to be connected over networks though, it because useful to have procedures that were remote to the main program. This way you could distribute the load across multiple computers, and maybe use powerful computers for some tasks and regular computers for others. Thus came about the concept of Remote Procedure Calls (RPC). RPC is a way of running procedures on remote computers by tricking the local processor into thinking the procedure is local to it.

Here’s how RPC works:

  1. Instead of a local procedure as usual, you have a stub procedure. This is a local procedure that takes input parameters meant for the remote procedure as though it were the remote procedure. As far as the main program is concerned the procedure is local.
    • To avoid confusion later on, we will refer to this stub as a client stub henceforth.
  2. The client stub procedure converts the input parameters into a form that can be understood by the remote computer. This is necessary as the parameters are in a location (memory space) that cannot be accessed by the remote computer, and also because the local and remote computers may use different ways of representing data (big-endian vs little-endian, for instance) and so the data may need conversion to a standard format. This process is called marshaling.
    • On Windows the client stub takes the input parameters (as in copies the values of the input parameters) and passes on to a Client Runtime Library (called rpcrt4.dll). This library does the marshaling.
    • This standard format is called a Network Data Representation (NDR) format. On Windows there are two marshaling “engines” that do this conversion. NDR20 is for 32-bit programs. NDR64 is for 64-bit programs (thought 64-bit programs can also use NDR20). Both marshaling engines are part of the Client Runtime Library.
  3. The client stub procedure uses the OS to contact the remote end and sends it messages. The OS in turns uses transport layer network protocols.
    • The client stub knows where the remote end is because it is either configured with the information or it looks up a central location to find the remote end. Usually the network address and port number(s) of the remote end.
      • For instance Windows domain joined computers can use Active Directory. In Windows 2000 domains, RPC services register under System\RPCServices.
      • This remote network address and port(s) is called an Endpoint.
    • The remote end usually has an RPC service listening on a designated port(s) and network address. This service allocates dynamic Endpoints for client stubs to connect to.
      • This service is called an Endpoint Mapper (EPM). In Windows you have service called the RPC Endpoint Mapper. It listens on port 135.
    • During this negotiation phase the EPM protocol messages contain items known as towers and floors. I am not too clear on what they are except that they are ways of representing RPC data and also for identifying the host address and port. When doing a network capture of RPC traffic these can be seen (the last link in the list below has examples of capturing RPC traffic).
    • On Windows the Client Runtime Library has three protocol engines – 1) a Connection RPC protocol engine (used when a connection-oriented protocol is required), 2) a Datagram RPC protocol engine (used when a connection-less protocol is required), and 3) a Local RPC protocol engine (used when the remote end is on the same host as the client). These protocol engines are what do the actual message passing.
    • Connection-oriented protocols used are TCP (dynamically assigned ports), SPX (Sequenced Packet Exchange), named pipes (port 445), or HTTP (ports 80, 443, 593). Connection-less protocols used are UDP (dynamically assigned ports) or CDP (Cluster Datagram Protocol).
  4. The remote end has a server stub that receives data from the local stub and unmarshals it. If required, the server stub converts the data to the remote machine specific format.
    • Server stubs register themselves with the Endpoint Mapper. Server stubs have a UUID (a well known GUID that identifies the application).
    • Once client stubs get an Endpoint from the Endpoint Mapper the client and server stubs talk to each other directly.
  5. The server stub then invokes the remote procedure with the data it has. As far as the remote procedure is concerned the main program is local to it. It is not aware that it is remote to the main program.
  6. Output from the remote procedure is passed on to the server stub which sends it to the client stub following a similar process as above (marshaling, unmarshaling).

The neat thing about RPC is that programmers can write distributed applications and don’t have to worry about the network details. In terms of the Internet Protocol suite, RPC sits in the Application layer.

Here are some good links on RPC: