clnttcp_create, clnttcp_callclntudp_bufcreate, clntudp_call
typedef struct __client {
AUTH *cl_auth;
struct clnt_ops {
/* wywolanie zdalnej procedury */
enum clnt_stat (*cl_call)();
/* przerwanie wolania procedury */
void (*cl_abort)();
/* pobiera kod bledu */
void (*cl_geterr)();
/* zwalnia pamiec rezultatow */
bool_t (*cl_freeres)();
/* usuwa strukture klienta */
void (*cl_destroy)();
/* operacja ioctl() */
bool_t (*cl_control)();
} *cl_ops;
caddr_t cl_private; - dane prywatne klienta (zalezne od protokolu)
} CLIENT;
cl_auth - dane do identyfikacji klientaclnt_ops - operacje klienta, wskazniki na funkcje zalezne od protokolucl_abort - w przypadku obu klientow (TCP, UDP) zmienna ta wskazuje na pusta funkcje. Prawdopodobnie funkcja przewidziana dla innych, niestandardowych protokolow.cl_freeres - funkcja powinna wywolac filtr XDR dla rezultatow z operacja XDR_FREEcl_control - funkcja umozliwiajaca sprawdzenie/ustawienie czasu czekania na odowiedz i pobrania adresu sieciowego serwera
struct ct_data {
int ct_sock;
struct sockaddr_in ct_addr;
char ct_mcall[MCALL_MSG_SIZE];
XDR ct_xdrs;
[...]
};
ct_sock - gniazdo klientact_addr - adres serweract_mcall - zakodowany standardowy fragment naglowkact_xdrs - potok komunikatow XDR
Tworzeniem klienta zajmuje sie funkcja:
CLIENT* clnttcp_create(struct sockaddr_in *raddr, u_long prog, u_long vers, int *sockp, u_int sendsz, u_int recvsz)
raddr - adres zdalny lub NULL
prog, vers - identyfikuja program na serwerze
sockp - wczesniej utworzone gniazdo lokalne lub -1 jesli procedura ma utworzyc nowe
sendsz, recvsz - wielkosci buforow TCP
bindresvport()), czyli zastrzezony dla root'a. Dla zwyklego uzytkownika bindresvport() zwroci kod bledu, jednak rezultat jej nie jest w ogole sprawdzany ani zapamietywany.
connect(); jesli nie udalo sie wczesniej przypisac numeru portu to connect() robi to automatycznie stosujac funkcje autobind()), ktore jest zamykane dopiero przez clnttcp_destroy().
clnttcp_call(CLIENT *h, u_long proc, xdrproc_t xdr_args, caddr_t args_ptr, xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout);
rm_xid w zakodowanym fragmencie naglowka(!!!), stad kazda wiadomosc ma unikalny (do pewnego stopnia) identyfikator. Operacja:u_long *msg_x_id = (u_long *)(ct->ct_mcall);--(*msg_x_id);
x_id (zazwyczaj wystarczy jeden raz).
accepted_reply: AR_results.where i AR_results.proc na adres bufora i funkcje rozkodowujaca rezultat, funkcja sprawdza poprawnosc rezultatow (tzn. czy procedura zostala wykonana i tozsamosc nadawcy) i rozkodowuje rezultaty wprost pod adres wskazany przez uzytkownika przy wywolaniu funkcji.
clnttcp_geterr, clnttcp_freeres, clnttcp_abort, clnttcp_control, clnttcp_destroy) sa bardzo proste, a ich dzialanie powinno byc oczywiste.
struct cu_data {
int cu_sock;
struct sockaddr_in cu_raddr;
XDR cu_outxdrs;
u_int cu_sendsz;
u_int cu_recvsz;
char *cu_outbuf;
char *cu_inbuf;
[...]
};
cu_sock - gniazdo klientacu_raddr - adres serweracu_outxdrs - potok XDRMEMcu_sendsz - rozmiar bufora wyjsciowegocu_recvsz - rozmiar bufora wejsciowegocu_outbuf - adres bufora wyjsciowegocu_inbuf - adres bufora wejsciowego
Warto wspomniec, ze powyzsza struktura oraz bufory wejsciowy i wyjsciowy znajduja sie w jednym bloku pamieci o rozmiarze sizeof(struct cu_data)+cu_sendsz+cu_recvsz, ktory jest pobierany jednym wywolaniem funkcji malloc.
CLIENT* clntudp_bufcreate(struct sockaddr_in *raddr, u_long prog, u_long vers, struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
Funkcja powyzsza jest wywolywana z jednolinijkowej funkcji clntudp_create(raddr, prog, vers, wait, sockp), ktora ustawia standardowe wartosci sendsz i recvsz.
Dzialanie jest podobne jak w przypadku TCP przed wywolaniem funkcji connect().W tym przypadku nie nawiazuje sie polaczenia. Funkcja tworzy potok XDRMEM, ktorym koduje poczatek naglowka i zapamietuje ten potok.
clntudp_call(CLIENT *h, u_long proc, xdrproc_t xdr_args, caddr_t args_ptr, xdrproc_t xdr_results, caddr_t results_ptr, struct timeval timeout)
rm_xid w zakodowanym fragmencie naglowka (w TCP bylo zmiejszane, ale i tak nie ma to znaczenia).
sendto() wysyla serwerowi wiadomosc, ustawia pola w accepted_reply: AR_results.where i AR_results.proc na adres bufora i funkcje rozkodowujaca rezultat.
select(). Jesli po ustalonym czasie odpowiedz nie nadejdzie to ponawiane jest wysylanie (jak kilka razy nie otrzyma sie dpowiedzi to zwracany jest blad).
recvfrom()) i jesli jego identyfikator x_id jest niezgodny z wyslanym przez klienta to dalej nasluchuje sie (porownanie to nie wymaga rozkodowania wiadomosci, gdyz w pamieci jest trzymany zakodowany identyfikator w naglowku komunikatu od klienta do serwera).
Tak jak w TCP: pozostale funkcje (clntudp_geterr, clntudp_freeres, clntudp_abort, clntudp_control, clntudp_destroy) sa bardzo proste.
int callrpc(char *host, u_long prognum, u_long versnum, u_long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out),
ktora sama tworzy klienta RPC (tylko dla protokolu UDP) i wywoluje clntudp_call. Na koniec dzialania tej funkcji struktura klienta nie jest usuwana i moze byc ponownie uzyta przy ponownym wywolaniu funkcji. Usuwana jest dopiero przy wywolaniu funkcji z innym serwerem, numerem programu lub wersji. Implementacja: plik rpc/clnt_simple.c
./rpc/* - implementacja RPC
/usr/include/rpc/*
/usr/info/libc.info - opis funkcji dotyczacych gniazd