RPC サーバーにユーザーファイル記述子を登録する方法、およびユーザーが定義したコールバックを提供する方法の例を示します。この例では、サーバーとクライアント両方での日時を監視できます。
このプログラムの makefile を次に示します。
RPCGEN = rpcgen CLIENT = todClient CLIENT_SRC = todClient.c timeofday_clnt.c CLIENT_OBJ = $(CLIENT_SRC:.c=.o) SERVER = todServer SERVER_SRC = todServer.c timeofday_svc.c SERVER_OBJ = $(SERVER_SRC:.c=.o) RPCGEN_FILES = timeofday_clnt.c timeofday_svc.c timeofday.h CFLAGS += -I. RPCGEN_FLAGS = -N -C LIBS = -lsocket -lnsl all: ./$(CLIENT) ./$(SERVER) $(CLIENT): timeofday.h $(CLIENT_OBJ) cc -o $(CLIENT) $(LIBS) $(CLIENT_OBJ) $(SERVER): timeofday.h $(SERVER_OBJ) cc -o $(SERVER) $(LIBS) $(SERVER_OBJ) timeofday_clnt.c: timeofday.x $(RPCGEN) -l $(RPCGEN_FLAGS) timeofday.x> timeofday_clnt.c timeofday_svc.c: timeofday.x $(RPCGEN) -m $(RPCGEN_FLAGS) timeofday.x> timeofday_svc.c timeofday.h: timeofday.x $(RPCGEN) -h $(RPCGEN_FLAGS) timeofday.x> timeofday.h clean: rm -f $(CLIENT_OBJ) $(SERVER_OBJ) $(RPCGEN_FILES)
timeofday.x ファイルは、この例の中でサーバーによって提供される RPC サービスを定義します。この例のサービスは、gettimeofday() および settimeofday() です。
program TIMEOFDAY { version VERS1 { int SENDTIMEOFDAY(string tod) = 1; string GETTIMEOFDAY() = 2; } = 1; } = 0x20000090;
userfdServer.h ファイルは、この例におけるソケットで送られるメッセージの構造を定義します。
#include "timeofday.h" #define PORT_NUMBER 1971 /* * 接続用のデータを保存するのに使用される構造 * (user fds test). */ typedef struct { /* * このリンクのコールバック登録の ID */ svc_input_id_t in_id; svc_input_id_t out_id; /* * この接続から読み取られるデータ */ char in_data[128]; /* * この接続に書き込まれるデータ */ char out_data[128]; char* out_ptr; } Link; void socket_read_callback(svc_input_id_t id, int fd, unsigned int events, void* cookie); void socket_write_callback(svc_input_id_t id, int fd, unsigned int events, void* cookie); void socket_new_connection(svc_input_id_t id, int fd, unsigned int events, void* cookie); void timeofday_1(struct svc_req *rqstp, register SVCXPRT *transp);
todClient.c ファイルは、クライアントで日時がどのように設定されるかを示します。このファイルでは、RPC はソケットとともにでも、ソケットなしでも使用されます。
#include "timeofday.h" #include <stdio.h> #include <netdb.h> #define PORT_NUMBER 1971 void runClient(); void runSocketClient(); char* serv_addr; void usage() { puts("Usage: todClient [-socket] <server_addr>"); exit(2); } int main(int argc, char* argv[]) { CLIENT* clnt; int sockClient; if ((argc != 2) && (argc != 3)) usage(); sockClient = (strcmp(argv[1], "-socket") == 0); /* * ソケット (sockClient) の使用を選択する。 * ソケットが使用できない場合は、 * ソケットなしで RPC (runClient) を使用する。 */ if (sockClient && (argc != 3)) usage(); serv_addr = argv[sockClient? 2:1]; if (sockClient) { runSocketClient(); } else { runClient(); } return 0; } /* * ソケットなしで RPC を使用する。 */ void runClient() { CLIENT* clnt; char* pts; char** serverTime; time_t now; clnt = clnt_create(serv_addr, TIMEOFDAY, VERS1, "tcp"); if (NULL == clnt) { clnt_pcreateerror("Cannot connect to log server"); exit(1); } time(&now); pts = ctime(&now); printf("Send local time to server\n"); /* * ローカル時刻を設定し、この時刻をサーバーへ送信する。 */ sendtimeofday_1(pts, clnt); /* * サーバーに現在時刻を要求する。 */ serverTime = gettimeofday_1(clnt); printf("Time received from server: %s\n", *serverTime); clnt_destroy(clnt); } /* * ソケット付きの RPC を使用する。 */ void runSocketClient() /* * ソケットを作成する。 */ { int s = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in sin; char* pts; char buffer[80]; int len; time_t now; struct hostent* hent; unsigned long serverAddr; if (-1 == s) { perror("cannot allocate socket."); return; } hent = gethostbyname(serv_addr); if (NULL == hent) { if ((int)(serverAddr = inet_addr(serv_addr)) == -1) { puts("Bad server address"); return; } } else { memcpy(&serverAddr, hent->h_addr_list[0], sizeof(serverAddr)); } sin.sin_port = htons(PORT_NUMBER); sin.sin_family = AF_INET; sin.sin_addr.s_addr = serverAddr; /* * ソケットを接続する。 */ if (-1 == connect(s, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in))) { perror("cannot connect the socket."); return; } time(&now); pts = ctime(&now); /* * ソケット上にメッセージを書き込む。 * メッセージはクライアントの現在時刻。 */ puts("Send the local time to the server."); if (-1 == write(s, pts, strlen(pts)+1)) { perror("Cannot write the socket"); return; } /* * ソケット上のメッセージを読み取る。 * メッセージはサーバーの現在時刻。 */ puts("Get the local time from the server."); len = read(s, buffer, sizeof(buffer)); if (len == -1) { perror("Cannot read the socket"); return; } puts(buffer); puts("Close the socket."); close(s); }
todServer.c ファイルは、サーバーサイドからの timeofday サービスの使用法を示します。
#include "timeofday.h" #include "userfdServer.h" #include <stdio.h> #include <errno.h> #define PORT_NUMBER 1971 int listenSocket; /* * RPC サーバーの実装 */ int* sendtimeofday_1_svc(char* time, struct svc_req* req) { static int result = 0; printf("Server: Receive local time from client %s\n", time); return &result; } char ** gettimeofday_1_svc(struct svc_req* req) { static char buff[80]; char* pts; time_t now; static char* result = &(buff[0]); time(&now); strcpy(result, ctime(&now)); return &result; } /* *ソケットサーバーの実装 */ int create_connection_socket() { struct sockaddr_in sin; int size = sizeof(struct sockaddr_in); unsigned int port; /* * ソケットの作成 */ listenSocket = socket(PF_INET, SOCK_STREAM, 0); if (-1 == listenSocket) { perror("cannot allocate socket."); return -1; } sin.sin_port = htons(PORT_NUMBER); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; if (bind(listenSocket, (struct sockaddr*)&sin, sizeof(sin)) == -1) { perror("cannot bind the socket."); close(listenSocket); return -1; } /* * サーバーはクライアントの接続 * が作成されるのを待機する。 */ if (listen(listenSocket, 1)) { perror("cannot listen."); close(listenSocket); listenSocket = -1; return -1; } /* * svc_add_input は、待機しているソケット上に、 * 読み取りコールバック、socket_new_connection を登録する。 * 新しい接続が保留状態の時に、 * このコールバックが呼び出される。 */ if (svc_add_input(listenSocket, POLLIN, socket_new_connection, (void*) NULL) == -1) { puts("Cannot register callback"); close(listenSocket); listenSocket = -1; return -1; } return 0; } /* * socket_new_connection コールバック関数の定義。 */ void socket_new_connection(svc_input_id_t id, int fd, unsigned int events, void* cookie) { Link* lnk; int connSocket; /* * ソケット上で接続が保留状態にある時に、 * サーバーが呼び出される。この接続を今受け付ける。 * コールは非ブロッキング。 * このコールを扱うためにソケットを作成する。 */ connSocket = accept(listenSocket, NULL, NULL); if (-1 == connSocket) { perror("Server: Error: Cannot accept a connection."); return; } lnk = (Link*)malloc(sizeof(Link)); lnk->in_data[0] = 0; /* * 新規のコールバック socket_read_callback が作成された。 */ lnk->in_id = svc_add_input(connSocket, POLLIN, socket_read_callback, (void*)lnk); } /* * 新規のコールバックsocket_read_callback が定義される */ void socket_read_callback(svc_input_id_t id, int fd, unsigned int events, void* cookie) { char buffer[128]; int len; Link* lnk = (Link*)cookie; /* * メッセージを読み取る。この読み取りコールはブロックは行わない。 */ len = read(fd, buffer, sizeof(buffer)); if (len> 0) { /* * データを取得した。このソケット接続に * 関連付けられたバッファー内にそのデータをコピーする。 */ strncat (lnk->in_data, buffer, len); /* * 完全なデータを受信したかどうかをテストする。 * 完全なデータでない場合は、これは部分的な読み取りである。 */ if (buffer[len-1] == 0) { char* pts; time_t now; /* * 受信した日時を出力する。 */ printf("Server: Got time of day from the client: \n %s", lnk->in_data); /* * 応答データをセットアップする * (サーバーの現在の日時)。 */ time(&now); pts = ctime(&now); strcpy(lnk->out_data, pts); lnk->out_ptr = &(lnk->out_data[0]); /* * 応答の書き込み時にブロックを行わない * 書き込みコールバック (socket_write_callback) を登録する。 * ソケットへの書き込みアクセス権を保持している場合は、 * POLLOUT を使用することができる。 */ lnk->out_id = svc_add_input(fd, POLLOUT, socket_write_callback, (void*)lnk); } } else if (len == 0) { /* * 相手側でソケットがクローズされた。ソケットをクローズする。 */ close(fd); } else { /* * ソケットが相手側によりクローズされているか? */ if (errno != ECONNRESET) { /* * クローズされていない場合は、これはエラーである。 */ perror("Server: error in reading the socket"); printf("%d\n", errno); } close(fd); } } /* * socket_write_callback を定義する。 * ソケットへの書き込みアクセス権を保持している場合は、 * このコールバックが呼び出される。 */ void socket_write_callback(svc_input_id_t id, int fd, unsigned int events, void* cookie) { Link* lnk = (Link*)cookie; /* * 書き込む残りのデータ長を計算する。 */ int len = strlen(lnk->out_ptr)+1; /* * 時間をクライアントへ送信する */ if (write(fd, lnk->out_ptr, len) == len) { /* * すべてのデータが送られた。 */ /* * 2 つのコールバックの登録を解除する。 * この登録解除は、ファイル記述子がクローズされる時に * この登録が自動的に削除されるため、 * ここに例示されている。 */ svc_remove_input(lnk->in_id); svc_remove_input(lnk->out_id); /* * ソケットをクローズする。 */ close(fd); } } void main() { int res; /* * timeofday サービスおよびソケットを作成する */ res = create_connection_socket(); if (-1 == res) { puts("server: unable to create the connection socket.\n"); exit(-1); } res = svc_create(timeofday_1, TIMEOFDAY, VERS1, "tcp"); if (-1 == res) { puts("server: unable to create RPC service.\n"); exit(-1); } /* ユーザーファイル記述子をポーリングする。 */ svc_run(); }