Most network programming is done by writing application programs that call system-provided functions to perform specific network operations. For example, one function performs a TCP active open, another performs a TCP passive open, another sends data across a TCP connection, another sets specific protocol options (enable TCP's keepalive timer), and so on. In Section 1.15 we mentioned that two popular sets of functions for network programming (called APIs) are sockets and TLI. The API used by the client and the API used by the server can be different, as can the operating systems running on the client and server. It is the communication protocol and application protocol that determine if a given client and server can communicate with each other. A Unix client written in C using sockets and TCP can communicate with a mainframe server written in COBOL using some other API and TCP, if both hosts are connected across a network and both have a TCP/IP implementation.
Typically the client sends commands to the server, and the server sends replies back to the client. All the applications we've looked at so far— Ping, Traceroute, routing daemons, and the clients and servers for the DNS, TFTP, BOOTP, SNMP, Telnet, FTP, and SMTP— are built this way.
RPC, Remote Procedure Call, is a different way of doing network programming. A client program is written that just calls functions in the server program. This is how it appears to the programmer, but the following steps actually take place.
-
When the client calls the remote procedure, it's really calling a function on the local host that's generated by the RPC package. This function is called the client stub. The client stub packages the procedure arguments into a network message, and sends this message to the server.
-
A server stub on the server host receives the network message. It takes the arguments from the network message, and calls the server procedure that the application programmer wrote.
-
When the server function returns, it returns to the server stub, which takes the return values, packages them into a network message, and sends the message back to the client stub.
-
The client stub takes the return values from the network message and returns to the client application.
The network programming done by the stubs and the RPC library routines uses an API such as sockets or TLI, but the user application— the client program, and the server procedures called by the client— never deal with this API. The client application just calls the server procedures and all the network programming details are hidden by the RPC package, the client stub, and the server stub.
An RPC package provides numerous benefits.
-
The programming is easier since there is little or no network programming involved. The application programmer just writes a client program and the server procedures that the client calls.
-
If an unreliable protocol such as UDP is used, details like timeout and retransmission are handled by the RPC package. This simplifies the user application.
-
The RPC library handles any required data translation for the arguments and return values. For example, if the arguments consist of integers and floating point numbers, the RPC package handles any differences in the way integers and floating point numbers are stored on the client and server. This simplifies coding clients and servers that can operate in heterogeneous environments.
Details of RPC programming are provided in Chapter 18 of [Stevens 1990]. Two popular RPC packages are Sun RPC and the RPC package in the Open Software Foundation's (OSF) Distributed Computing Environment (DCE). Our interest in RPC is to see what the procedure call and procedure return messages look like for the Sun RPC package, since it's used by the Network File System, which we describe in this chapter. Version 2 of Sun RPC is defined in RFC 1057 [Sun Microsystems 1988a].
Sun RPC
Sun RPC comes in two flavors. One version is built using the sockets API and works with TCP and UDP. Another, called TI-RPC (for "transport independent"), is built using the TLI API and works with any transport layer provided by the kernel. From our perspective the two are the same, although we talk only about TCP and UDP in this chapter.
Figure 29.1 shows the format of an RPC procedure call message, when UDP is used.
Figure 29.1. Format of RPC procedure call message as a UDP datagram.
![](https://i-blog.csdnimg.cn/blog_migrate/82f575ed28a786a3ac2fef0401d1ea4b.jpeg)
The IP and UDP headers are the standard ones we showed earlier (Figures 3.1 and 11.2). What follows after the UDP header is defined by the RPC package.
The transaction ID (XID) is set by the client and returned by the server. When the client receives a reply it compares the XID returned by the server with the XID of the request it sent. If they don't match, the client discards the message and waits for the next one from the server. Each time the client issues a new RPC, it changes the XID. But if the client retransmits a previously sent RPC (because it hasn't received a reply), the XID does not change.
The call variable is 0 for a call, and 1 for a reply. The current RPC version is 2. The next three variables, program number, version number, and procedure number, identify the specific procedure on the server to be called.
The credentials identify the client. In some instances nothing is sent here, and in other instances the numeric user ID and group IDs of the client are sent. The server can look at the credentials and determine if it will perform the request or not. The verifier is used with Secure RPC, which uses DES encryption. Although the credentials and verifier are variable-length fields, their length is encoded as part of the field.
Following this are the procedure parameters. The format of these depends on the definition of the remote procedure by the application. How does the receiver (the server stub) know the size of the parameters? Since UDP is being used, the size of the UDP datagram, minus the length of all the fields up through the verifier, is the size of the parameters. When TCP is used instead of UDP, there is no inherent length, since TCP is a byte stream protocol, without record boundaries. To handle this, a 4-byte length field appears between the TCP header and the XID, telling the receiver how many bytes comprise the RPC call. This allows the RPC call message to be sent in multiple TCP segments, if necessary. (The DNS uses a similar technique; see Exercise 14.4.)
Figure 29.2 shows the format of an RPC reply. This is sent by the server stub to the client stub, when the remote procedure returns.
Figure 29.2. Format of RPC procedure reply message as a UDP datagram.
![](https://i-blog.csdnimg.cn/blog_migrate/0cff89d0379cecad0bc2d0f35d535476.jpeg)
The XID in the reply is just copied from the XID in the call. The reply is 1, which we said differentiates this message from a call. The status is 0 if the call message was accepted. (The message can be rejected if the RPC version number isn't 2, or if the server cannot authenticate the client.) The verifier is used with secure RPC to identify the server.
The accept status is 0 on success. A nonzero value can indicate an invalid version number or an invalid procedure number, for, example. As with the RPC call message, if TCP is used instead of UDP, a 4-byte length field is sent between the TCP header and the XID.