Remote Procedure Calls Ivor Page Local vs. Remote Calling Local Remote k = f(x) k = f_1(x) int f_1(x) f_1(x) stub stub Network Network call return int f(x) Systems SUN Open Network Computing (ONC). ONC RPC 4 supports TCP and UDP protocols. Protocol Compiler: rpcgen HP/Apollo Network Computing Architecture (NCA). Protocol Compiler: NIDL Data Transfer When passing arguments, the data must be mapped between machines (endian and other data representation differences). ONC has XDR, a machine independent data transfer language that is supported by mapping functions at both ends. NCS uses a “receiver makes it right” scheme, where the data receiver must handle the data mapping. Stateless vs. Stateful “Stateless” servers do not have any way to remember the history of previous RPC calls. Consider a database, where the client gave an RPC search call to locate a record, then gave another call to read or update that record. This server is “stateful.” If a stateless server crashes, the clients need only wait until it is “up” again to continue normal operation. Current transactions would be lost. A stateful server would need to log activities to be able to recover. Connection vs. Connectionless In a connection oriented protocol, low level transport functions do time-outs and retries. The protocol is said to be “reliable.” TCP is a connection oriented protocol. In a connectionless protocol, the clients have to handle communications problems on their own. We say these protocols are unreliable. UDP is connectionless. If a client doesn’t get a return within a certain time, it cannot tell if the server executed the RPC call. In a connectionless protocol, if a client makes n calls that each timeout and receives no return it does not know whether the server executed 0,1,…,n of them. Only when the client makes one call and receives one return can it tell how many calls the server executed. In a connection oriented protocol the low level functionality takes care of retries, but the client can still timeout (default timeout = 25 secs). On a timeout the client cannot tell if its call was received or not. Usually a timeout indicates a server failure or extreme network congestion. Idempotent Servers A server procedure is idempotent if a call can be repeated multiple times without the extra calls altering the server state. Such procedures are ideal for connectionless protocols. A server procedure that increments a count in the server is not idempotent. If a server procedure is not idempotent in an unreliable protocol, a waiting client cannot know if a call has been executed and cannot retry the call. How does it work? When a server program first runs, it registers its program and version number with the local RPC name server, or “port mapper.” A port number is assigned to the server. The server becomes a daemon process that “listens” on its port for RPC calls. When a client wishes to begin sending RPC’s to a server, it first consults the server’s portmapper, passing over the program number and version number. The portmapper returns the port address. The client calls the server’s procedures by passing the proc’ number and args to the port address . ONC RPC Example The client calls the server passing over an integer. The server keeps track of the total number of RPC calls that it has received and simply returns that number. We need three source files, one to explain to rpcgen the interface for the remote calls (count.x), one for the client (count_client.c), and one for the server containing the remote procedure (count_serv.c). rpcgen will generate header files and a main() for the server. File count.x (for rpcgen) program COUNTPROG { version COUNTVERS { int COUNT(int) =1; } =1; } = 0x20000099; /* server program name */ /* remote proc’ name, number */ /* server version number */ /* program number. You choose a unique number */ The remote procedure in the client will actually be as follows: int * count_1(int *, CLIENT *); The 2 arguments and the return value are passed using pointers. Program numbers 0X00000000 to 0X1FFFFFFF Defined by SUN 0X20000000 to 0X3FFFFFFF User-defined 0X40000000 to 0X5FFFFFFF Transient 0X60000000 to 0XFFFFFFFF Reserved /etc/rpc has a list of program numbers in use. There can be many versions of the same server in operation at once. Their version numbers distinguish them. What Can be Transferred? The xdr routines allow clients to send and receive all C built in types. A string can be sent or received. It is defined as an array of chars, terminated with ‘\0’. Structs can be sent and received. See the bank account rpc problem. File count_client.c #include <stdio.h> #include <rpc/rpc.h> #include "count.h" /* count.h is generated by rpcgen */ main() { CLIENT *cl; int *result; char *server; server =“pscedu1”; /* client handle */ /* host name */ /* Create client "handle" used for calling the server. We tell the RPC package to use the "tcp" protocol. */ cl = clnt_create(server, COUNTPROG, COUNTVERS, "tcp"); if (cl == NULL) { clnt_pcreateerror(server); /* print error message */ exit(1); } /* COUNTPROG and COUNTVERS are #defined in count.h */ /* make the RPC call and check return */ result = count_1(&x,cl); if (result == NULL) { clnt_perror(cl,server); exit(1); /* print error message */ } printf("\nResult was %d\n", *result); } /* end of client program */ File count_server.c #include <stdio.h> #include <rpc/rpc.h> #include "count.h" int * count_1(int *x) { static int result=0; result++; return (&result); } /* Implementation of the */ /* remote procedure "count" */ /* Produced by rpcgen */ /* this is the remote proc */ /* argument returned must */ /* be static */ /* must return an address * Compiling client and server First make count.h, count_clnt.c, and count_svc.c % rpcgen count.x This produces count_clnt.c, count_xdr.c, count.h, and count_svc.c. Now compile and link client % cc count_client.c count_clnt.c -o count_client Now compile and link server % cc count_server.c count_svc.c -o count_sever To run the server and client First we run the server % count_server & The server now waits for RPC's. Now we run the client % count_client & To run another client: % count_client & You can remove the server by using: % kill -9 pid where pid is got by using the ps command. The Bank Account RPC Project In this project, the clients simulate ATM machines, the bank customers, and the bank manager. Clients send transactions to the server. The server manages the bank accounts and deals with the transactions, sending back balances, etc. The main additions in the bank account problem are: • • Many RPC functions in the server Use of structs to pass data to remote proc’s. When structs are used, rpcgen generates an additional file for xdr data mapping. Files We start with files: 1. accounts.x 2. client.c 3. server.c 4. defines.h the protocol def’n file the client code the server code some #defines used by all The Protocol Definition File accounts.x struct bal_trans { int account_number; int pin; }; /* three structs */ /* for data to be */ /* sent to server */ struct dep_trans { int account_number; int pin; long amount; }; struct open_trans { int pin; long amount; }; program ACCOUNTS { version ACCOUNTSVERS { int INITIALIZE(int) int OPEN(open_trans) long BALANCE(bal_trans) long DEPOSIT(dep_trans) long WITHDRAWL(dep_trans) long CLOSE(bal_trans) int EXIT(int) } = 1; } = 0x2000003; = = = = = = = 1; 2; 3; 4; 5; 6; 7; RPC Services int *initialize_1(int *t, CLIENT *cl); Open the bank, giving the master pin. Open the bank_account file at the server, read it and initialize. A 1 is returned if successful. If the bank is already open - a second client calls initialize - nothing happens. int *open_1(open_trans *t, CLIENT *cl); Open an account giving pin and initial deposit. Account number is returned. long *balance_1(bal_trans *t, CLIENT *cl); Balance enquiry, suppy account number and pin. Balance is returned. long *deposit_1(dep_trans *t, CLIENT *cl); Deposit, giving account number, pin and amount New balance is returned. long *withdrawal_1(dep_trans *t, CLIENT *cl); Withdrawl, giving account number, pin and amount. New balance is returned long *close_1(bal_trans t) Close an account, giving account number and pin. Closing balance is returned. int *exit_1(int *dummy) Close the bank, saving all open account data to the bank_account file. Error Returns In all RPC calls an error indicator may be returned if something is wrong. Here is defines.h: #define #define #define #define #define #define #define #define #define #define MAX_ACCOUNTS BANK_FULL ACCOUNT_NOT_OPEN WRONG_PIN INSUFFICIENT_FUNDS FILE_OPEN_ERROR FILE_CLOSE_ERROR WRONG_MASTER_PIN INVALID_ACCOUNT_NUMBER MASTER_PIN 100 -1 -2 -3 -4 -5 -6 -7 -8 1873 makefile for Client & server #makefile for RPC project COMPILER = cc CFLAGS = -c OFLAGS = -o accounts_xdr.c accounts.h accounts_clnt.c accounts_svc.c: accounts.x rpcgen accounts.x client: client.o accounts_clnt.o accounts_xdr.o $(COMPILER) $(OFLAGS) client client.o accounts_clnt.o accounts_xdr.o \ server: accounts.o accounts_svc.o accounts_xdr.o $(COMPILER) $(OFLAGS) server server.o accounts_svc.o \ accounts_xdr.o client.o: client.c defines.h accounts.h $(COMPILER) $(CFLAGS) client.c accounts_clnt.o: accounts_clnt.c accounts.h $(COMPILER) $(CFLAGS) accounts_clnt.c accounts_xdr.o: accounts_xdr.c accounts.h $(COMPILER) $(CFLAGS) accounts_xdr.c server.o: server.c accounts.h defines.h $(COMPILER) $(CFLAGS) server.c accounts_svc.o: accounts_svc.c accounts.h $(COMPILER) $(CFLAGS) accounts_svc.c rpcgen accounts.x accounts_clnt.c accounts_xdr.c cc compiler cc compiler accounts_clnt.o accounts.h accounts_xdr.o client.c accounts_svc.c cc compiler accounts_svc.o server.c cc compiler cc compiler client.o server.o cc (linker) client cc (linker) server