Remote Procedure Calls
Shyan-Ming Yuan
CS Department, NYCU
[email protected]
Problems with the Socket API
The socket interface forces a read/write
mechanism.
Programming is often easier with a functional
interface.
It is desirable to make a network program
looks more like a traditional procedure-call
based program.
Remote Procedure Call (RPC) was then
proposed by Birrell & Nelson (1984)
It is a mechanism to call procedures on other
processes which may even be in other machines.
The Regular Procedure Call
A CPU Instruction Set always contains Call &
Return instructions
A Compiler usually translates a procedure call to
the following works:
Input parameters passing
Local variables handling
Output parameters passing
A Procedure Call Example
A statement of x = f(a, “test”, 5)
A C compiler will parse it and generate codes to
Push the constant value 5 into the stack;
Push the address of the string “test” into the
stack;
Push the current value of a into the stack;
Generate a Call instruction to the function f.
In compiling the function f, the C compile
generates codes to
Push registers that will be used inside the f into
the stack for saving their original values.
Adjust the stack to make room for local and
temporary variables.
The RPC Implementation
Most architectures (OS+HW) do not provide
support for RPCs
In general, an RPC is implemented by simulating
it with
Local procedure calls and
Socket APIs.
Therefore, RPC is usually a language level
construct instead of an operating system
construct.
Most RPC implementations provide tools to
create stub functions.
Stub functions contain the interfaces of remote
functions.
The RPC Data Flows
1. Client calls stub (parameters on stack)
The RPC Data Flows
2. Stub marshals parameters to a network
message
Marshalling = put parameters in a form suitable for transmission over a network (serialized)
The RPC Data Flows
3. A network message is sent to the server
The RPC Data Flows
4. The network message is received and
passed to the server stub
The RPC Data Flows
5. Unmarshal the message into parameters and
call the server function
The RPC Data Flows
6. Return from the server function
The RPC Data Flows
7. Marshal the return value and send the
reply message
The RPC Data Flows
8. Transfer message over network
The RPC Data Flows
9. Receive the reply message by the client
stub
The RPC Data Flows
10. Unmarshal return value(s) and return to
client code
The Client Stub
A client stub function is a proxy for a remote
procedure.
It has the same interface as the remote function.
It looks and feels like the remote function to the
programmer.
The real function of a client stub is to
Marshal parameters into a message;
Send the message to the server;
Wait for a response from the server;
Unmarshal the response into the return data and
return to the client;
Generate exceptions if problems arise.
The Server Stub
A server stub contains two parts:
Dispatcher – the listener
Receives client requests.
Identifies the appropriate service
function (method.)
Skeleton – the unmarshaller &
caller
Unmarshals a client request into
parameters.
Calls the local server procedure.
Marshals the response & sends it back to
the dispatcher.
All this is invisible to the
programmer
The RPC Benefits
RPC gives programmers a procedure call
paradigm.
It simplifies the efforts for writing network
applications.
It hides all network related codes into stub
functions
Application programmers don’t have to worry
about details of
Sockets, port numbers, byte ordering, …
Where is RPC in the OSI model?
Layer 5: Session layer: Connection
management
Layer 6: Presentation: Marshaling/data
The RPC Challenges
Parameter passing:
Pass by value can be achieved by copying data to
the network message
Pass by reference makes no sense without shared
memory
Can only support pointerless representation
Service binding: How to locate the server
endpoint?
A Central DB or local DBs (each host has its own)
may be used.
Transport protocol:
TCP? UDP? Both?
When things go wrong?
Pass by Reference
To implement pass by reference
Copy all referenced items to a message buffer
Send the whole message buffer to the server
Unmarshal items in the server stub
Pass the local (server side) pointers to the
service function
Send back items with new values
To support complex data structure
Copy the data structure to a pointless
representation
Transmit
Reconstruct the data structure with local
pointers in the server
When things go wrong
Semantics of remote procedure calls
Local procedure call: exactly once
Most RPC systems will offer either
at least once semantics or at most once semantics
Decision usually is based on the application
requirement
idempotent functions: may be executed any
number of times without creating side effects.
non-idempotent functions: may generate side-
effects when executed more than once.
Ideally – design your application to be
idempotent and stateless
Not always easy!
RPC Programming
Language support
Many programming languages have no language-
level concept of remote procedure calls
C, C++, Java (J2SE 5.0 and earlier versions), …
These compilers will not automatically generate client
and server stubs
Some languages have support that enables RPC
Java, Python, Haskell, Go, Erlang
But we may need to deal with heterogeneous
environments (e.g., Java communicating with a Python
service)
Common solution
Interface Definition Language (IDL): describes
remote procedures
Interface Definition Language
(IDL)
Allow programmer to specify remote
procedure interfaces
names, parameters, return values
Pre-compiler can use this to generate client
and server stubs
Marshaling codes
Unmarshalling codes
Network transport routines
Confirm to defined interfaces
An IDL looks similar to function prototypes
RPC Compiler
Sending Data Over Network
There is no incompatibility problems when
passing data among procedures on a local
system
However, remote machines may have:
Different byte ordering
Different sizes of integers and other types
Different floating point representations
Different character sets
Alignment requirements
Data Represent: Serialization
Need standard encoding to enable
communication between heterogeneous
systems
Serialization
Convert data into a pointerless format: an array
of bytes
Examples
XDR (eXternal Data Representation), used by
ONC RPC
JSON (JavaScript Object Notation)
W3C XML Schema Language
ASN.1 (ISO Abstract Syntax Notation)
Data Serializations
Implicit typing
only values are transmitted, not data types or
parameter info
e.g., ONC XDR (RFC 4506)
Explicit typing
Type is transmitted with each value
e.g., ISO’s ASN.1, XML, protocol buffers, JSON
Marshaling vs. serialization – almost synonymous:
Serialization: converting an object into a sequence
of bytes that can be sent over a network
Marshaling: bundling parameters into a form that
can be reconstructed (un-marshalled) by another
process. May include object ID or other state.
ONC RPC
RPC for Unix System V, Linux, BSD, OS X
ONC = Open Network Computing
Created by Sun Microsystem
RFC 1831 (1995), RFC 5531 (2009)
Remains in use mostly because of NFS
(Network File System)
Interfaces defined in an Interface Definition
Language (IDL)
IDL compiler is called the rpcgen
Build client server application by
rpcgen
Client Client
application interface
Q_clnt.c
C
compiler
client
Q.h
Xdr run time
Q.x rpcgen library
Q_xdr.c
C
compiler
server
Q_svc.c
Remote Server
procedures interface
Input and Output of rpcgen
Rpcgen reads an input specification file Q.x
It contains specifications of remote procedures
Rpcgen then generates 4 output files
Q.h contains declarations of constants and
types used in the code generated for both client
and server applications
Q_xdr.c contains XDR procedure calls used in
the client and server to marshal and un-marshal
arguments
Q_clnt.c contains client-side stub functions
Q_svc.c contains server-side stub functions
8 steps to build an RPC
application
Build and test a conventional application that
solve the problem.
Divide the program by choosing a set of
procedures to move to a remote host. Place
the selected procedures in a separate file.
Write an rpcgen specification for the remote
program.
Run rpcgen to check the spec and, if valid,
generate the 4 source files that will be used in
the client and server.
Write stub interface routines for the client and
server
1. Build A Dictionary
Application
The Dict App supports 5 one-letter commands
argument Meaning
comman
d
I -none- Initialize the database by
removing all words
i word Insert the word into the database
d word delete the word from the
database
The dict.c
l has main()
word Lookand 5 word
up the sub-functions
in the database
nextin()
q reads a command
-none- quit from the next input
line
initw() initialize the dictionary as an empty one
insertw() insert a word in the dictionary
deletew() delete a word from the dictionary
2. Divide The dict.c Into Two
Parts
Since the nextin() needs access the standard
input, it must be kept with the main program in
the local host.
Rule-1: procedures that perform I/O or access local
files usually are not moved to a remote host.
The lookup needs to search all words in the
dictionary, it should be kept in the same host as
the dictionary.
Rule-2: procedures should execute on the same
host as the data they access. Passing large data
structures as arguments to remote host is very
inefficient.
A good partition of the dictionary program is to
2. The new dictionary source
files
The new dictionary application now has two
source files
dict1.c contains the main() and the nextin()
dict2.c contains the dictionary and 4 sub-
functons of
initw(),
Client on host 1 insert(), delete(), andRemote
lookup()
program on host 2
calls to remote
procedures
initw
main
data
insertw
structures
deletw for
dictionary
nextin lookupw
3. Create An rpcgen
Specification
The rdict.x file must contain
all constants defined in the original source files
const MAXWORD = 50; // maximum length of a command
or word
const DICTSIZ = 100; // number of entries in dictionary
all data types used in arguments of remote
procedures
struct example { // unused structure declared here to
int exfield1; // illustrate how rpcgen builds
XDR
char exfield2; // routines to convert structures.
};
Declaration of remote programs, the procedures
contained in each program, and the types of their
3. The Remote Program
Declaration
//--------------------------------------------------------------------------------------------------
// RDICTPROG - remote program that provides insert, delete, and lookup
//--------------------------------------------------------------------------------------------------
program RDICTPROG { // name of remote program (not used)
version RDICTVERS { // declaration of version (see below)
int INITW(void) = 1; // first procedure in this program
int INSERTW(string) = 2; // second procedure in this program
int DELETEW(string) = 3; // third procedure in this program int
LOOKUPW(string) = 4; // fourth procedure in this program
} = 1; // definition of the program version
} = 0x30090949; // remote program number (must be unique)
4. Run rpcgen
In linux, the command syntax is:
rpcgen rdict.x
The rpcgen then generates
rdict.h, rdict_clnt.c, rdict_svc.c, and
rdict_xdr.c
5. Write Stub Interface
Procedures
The rdict_clnt.c and rdict_svc.c generated by
rpcgen do not form complete source
programs.
There are client-side and server-side interface
routines that need to be written by the
programmer.
On the client side, the main of the dict1.c
must call interface procedures using the same
procedure names and argument types as it
originally used.
Thus, the client-side interface procedures
should have the same interface as the original
sub-functions.
6. Compile and Link the
Client
Before build the client application, the dict1.c
needs to be revised.
It needs to include the rpc header files
<rpc/rpc.h> and “rdict.h”
It also needs to declare and initialize a handle
that the RPC communication routines can use to
communicate with the server
#include Most clients declare the handle using the defined type
<rpc/rpc.h>
#include CLIENT,
"rdict.h“ and initialize the handle by calling the RPC library
#define routine,
RMACHINE "localhost"
clnt_create. // name of remote machine
CLIENT *handle; // handle for remote procedure
main(int
Int The rdict.c
argc, charis derived from the dict1.c
*argv[])
{ …..
handle = clnt_create(RMACHINE, RDICTPROG, RDICTVERS, "tcp");
if (handle == 0) { printf("Could not contact remote program.\n"); exit(1);
}
6. Build the Client Application
Compiling client side source files by
cc –c rdict_clnt.c
cc –c rdict_xdr.c
cc –c rdict_cif.c
cc –c rdict.c
Link and build the client by
cc –o rdict rdict.o rdict_clnt.o rdict_xdr.o
rdict_cif.o
7. Compile and Link the
Server
Similarly to the client, the dict2.c also needs to
be revised.
It needs to include the rpc header file files
<rpc/rpc.h> and “rdict.h”
The revised version is rdict_srp.c
Compiling server side source files by
cc –c rdict_svc.c
cc –c rdict_xdr.c
cc –c rdict_sif.c
cc –c rdict_srp.c
Link and build the server by
cc –o rdictd rdict_svc.o rdict_xdr.o rdict_sif.o
rdict_srp.o
8. Start the Server and the
Client
The server must be started before a client
tries to contact it.
On a Linux system, we may use the command:
./rdictd &
The client then can be started either in the
same host or in another machine.
./rdict
The ONC RPC Binding
The server actions:
The server stub creates a socket and binds any
available local port to it.
It then calls the svc_register() to register
program#, port #
The port mapper, rpcbind (portmap on older Linux
systems), will be contacted.
It keeps track of{program #, version #, protocol} of all
registered servers.
The server then listens and waits to accept
connections.
The client actions:
The client calls clnt_create() with:
Name of server, Program #, Version #, Protocol#
The ONC RPC Binding