Remote Procedure Calls

advertisement
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
Download