So the IP protocol handles getting individual packets between hosts that are communicating, the TCP protocol on top of that handles fragmentation and keeping the stream in sync. On top of TCP, you may impose your own record types (as we do in RPC) to fit your application. Thus the structure of a data packet is:
application data |
TCP data |
IP data |
The application portion of the payload for RPC will look something like this:
RPC # | count | arguments |
To generate a stub, we give the RPC stub generator a specification, much like a declaration of a function in C. For example, we could have the following declaration:
While this looks like C declaration, it isn't. The IN and OUT declarations tell the RPC stub generator the types of arguments these are. There can also parameters that are declared IN and OUT at the same time.int my_function(IN int x, IN char *y, OUT char *z);
Given such a declaration, the stub generator will generate several things in C code (or another language):
my_function()
across the network
my_function()
my_function()
that has the required parameters
my_function()
. When writing the server,
we can fill in the empty function my_function()
to do the
work we want, and trust that RPC will handle the rest (networking, data
handling).
How does RPC know which function to call on the server? That's the RPC #
that we saw in the above payload. The server gets a request, and then it
checks to see if it has a function (such as my_function()
registered with that RPC #. If it does, then it unpacks the arguments in
a way particular to that function, and calls the function that the user
wrote. It takes the output from the user function, packages it, and sends
it back to the client.
Fortunately, RPC handles a lot of this for us. It uses functions such as the following to translate long and short integers:
long netLong = htonl(long hostLong)
long hostLong = ntohl(long netLong)
short netShort = htons(short hostShort)
short hostShort = ntohs(short netShort)
htonl()
converts a long integer (4 bytes) into
a standardized (which is usually big-endian) so it can be transported
across the network. The function ntohl()
does the inverse.
Additionally, RPC knows how to handle other primitives like char *. However,
if you are going to use any fancy data structures that RPC doesn't know about,
you have to write your own routines to handle packing and unpacking the data.
The process of taking data from a host and converting it for transport across the network is called data marshalling. The reverse process of taking data from the network and converting it to the host for processing is called data unmarshalling.
RPC handles these things for us, in the following manner. For the client, when an RPC function is called:
And for the server, it does the following:
bsy+cse127w02@cs.ucsd.edu, last updated
email bsy.