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();
}