在查看libc6-dev軟件包提供的工具(用 dpkg -L libc6-dev 命令)的時候,發現此軟件包提供了一個有用的工具rpcgen命令。
通過rpcgen的man手冊看到此工具的作用是把RPC源程序編譯成C語言源程序,從而輕松實現遠程過程調用。
下面的例子程序的作用是客戶端程序取中心服務器上時間的,編程過程如下:
先編寫一個 “ RPC 語言 ” ( RPC Language ( Remote Procedure Call Language ) ) 的源文件 test.x ,文件后綴名為 x 。
源代碼如下:
說明:這里數字87654321是RPC程序編號,還有VERSION版本號為1,都是給RPC服務程序用的。同時指定程序接受一個字符串參數。
運行這個命令:
將生成三個源文件:
源文件test_clnt.c 內容如下:
說明:這是一個客戶端調用函數,即客戶端代碼需要用到此函數。
源文件test.h內容如下:
說明:這里定義了一些公用頭文件。
源文件test_svc.c內容如下:
說明:這是一個標準的服務器端代碼。
運行下列命令生成一個客戶端源文件test_client.c:
源代碼test_client.c如下:
運行這個命令生成服務端源文件test_srv_func.c:
源文件test_srv_func.c內容如下:
說明:這是一個服務器端調用的函數。
至此,我們就可以編譯生成程序來運行了。
用下面的命令編譯生成服務端程序test_server:
用下面的命令編譯生成客戶端程序test_client:
運行下列命令啟動服務端:
運行下列命令可以進行客戶端測試:
但是由于現的的服務端沒有處理客戶端請求,所以這樣的程序還不能完成任何工作。
下面我們先給服務端程序加上代碼,使這個服務器能完成一定的工作。即修改 test_srv_func.c ,在 “ * insert server code here ” 后面加上取時間的代碼,即修改后的 test_srv_func.c 代碼如下:
再修改客戶端代碼以顯示服務器端返回的內容,即修改test_client.c源文件,只需要修改其中的函數testprog_1,修改后如下:
重新運行上述編譯命令編譯生成程序:
啟動服務端程序后運行客戶端程序如下:
為了省略每次輸入gcc命令的麻煩,也為了維護我們的工程,可以運行下列命令生成一個Makefile文件:
生成的Makefile內容如下:
由于我們手工生成了源文件,所以要修改一下這個Makefile,修改后如下:
通過rpcgen的man手冊看到此工具的作用是把RPC源程序編譯成C語言源程序,從而輕松實現遠程過程調用。
下面的例子程序的作用是客戶端程序取中心服務器上時間的,編程過程如下:
先編寫一個 “ RPC 語言 ” ( RPC Language ( Remote Procedure Call Language ) ) 的源文件 test.x ,文件后綴名為 x 。
源代碼如下:
program TESTPROG { ?? version VERSION { ???? string TEST(string) = 1; ?? } = 1; } = 87654321; |
運行這個命令:
rpcgen test.x |
test_clnt.c? test.h? test_svc.c |
/* ?* Please do not edit this file. ?* It was generated using rpcgen. ?*/ #include <memory.h> /* for memset */ #include "test.h" /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; char ** test_1(char **argp, CLIENT *clnt) { ??????? static char *clnt_res; ??????? memset((char *)&clnt_res, 0, sizeof(clnt_res)); ??????? if (clnt_call (clnt, TEST, ??????????????? (xdrproc_t) xdr_wrapstring, (caddr_t) argp, ??????????????? (xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res, ??????????????? TIMEOUT) != RPC_SUCCESS) { ??????????????? return (NULL); ??????? } ??????? return (&clnt_res); } |
源文件test.h內容如下:
/* ?* Please do not edit this file. ?* It was generated using rpcgen. ?*/ #ifndef _TEST_H_RPCGEN #define _TEST_H_RPCGEN #include <rpc/rpc.h> #ifdef __cplusplus extern "C" { #endif #define TESTPROG 87654321 #define VERSION 1 #if defined(__STDC__) || defined(__cplusplus) #define TEST 1 extern? char ** test_1(char **, CLIENT *); extern? char ** test_1_svc(char **, struct svc_req *); extern int testprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define TEST 1 extern? char ** test_1(); extern? char ** test_1_svc(); extern int testprog_1_freeresult (); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_TEST_H_RPCGEN */ |
源文件test_svc.c內容如下:
/* ?* Please do not edit this file. ?* It was generated using rpcgen. ?*/ #include "test.h" #include <stdio.h> #include <stdlib.h> #include <rpc/pmap_clnt.h> #include <string.h> #include <memory.h> #include <sys/socket.h> #include <netinet/in.h> #ifndef SIG_PF #define SIG_PF void(*)(int) #endif static void testprog_1(struct svc_req *rqstp, register SVCXPRT *transp) { ??????? union { ??????????????? char *test_1_arg; ??????? } argument; ??????? char *result; ??????? xdrproc_t _xdr_argument, _xdr_result; ??????? char *(*local)(char *, struct svc_req *); ??????? switch (rqstp->rq_proc) { ??????? case NULLPROC: ??????????????? (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); ??????????????? return; ??????? case TEST: ??????????????? _xdr_argument = (xdrproc_t) xdr_wrapstring; ??????????????? _xdr_result = (xdrproc_t) xdr_wrapstring; ??????????????? local = (char *(*)(char *, struct svc_req *)) test_1_svc; ??????????????? break; ??????? default: ??????????????? svcerr_noproc (transp); ??????????????? return; ??????? } ??????? memset ((char *)&argument, 0, sizeof (argument)); ??????? if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { ??????????????? svcerr_decode (transp); ??????????????? return; ??????? } ??????? result = (*local)((char *)&argument, rqstp); ??????? if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) { ??????????????? svcerr_systemerr (transp); ??????? } ??????? if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { ??????????????? fprintf (stderr, "%s", "unable to free arguments"); ??????????????? exit (1); ??????? } ??????? return; } int main (int argc, char **argv) { ??????? register SVCXPRT *transp; ??????? pmap_unset (TESTPROG, VERSION); ??????? transp = svcudp_create(RPC_ANYSOCK); ??????? if (transp == NULL) { ??????????????? fprintf (stderr, "%s", "cannot create udp service."); ??????????????? exit(1); ??????? } ??????? if (!svc_register(transp, TESTPROG, VERSION, testprog_1, IPPROTO_UDP)) { ??????????????? fprintf (stderr, "%s", "unable to register (TESTPROG, VERSION, udp)."); ??????????????? exit(1); ??????? } ??????? transp = svctcp_create(RPC_ANYSOCK, 0, 0); ??????? if (transp == NULL) { ??????????????? fprintf (stderr, "%s", "cannot create tcp service."); ??????????????? exit(1); ??????? } ??????? if (!svc_register(transp, TESTPROG, VERSION, testprog_1, IPPROTO_TCP)) { ??????????????? fprintf (stderr, "%s", "unable to register (TESTPROG, VERSION, tcp)."); ??????????????? exit(1); ??????? } ??????? svc_run (); ??????? fprintf (stderr, "%s", "svc_run returned"); ??????? exit (1); ??????? /* NOTREACHED */ } |
運行下列命令生成一個客戶端源文件test_client.c:
rpcgen -Sc -o test_client.c test.x |
/* ?* This is sample code generated by rpcgen. ?* These are only templates and you can use them ?* as a guideline for developing your own functions. ?*/ #include "test.h" void testprog_1(char *host) { ??????? CLIENT *clnt; ??????? char * *result_1; ??????? char * test_1_arg; #ifndef DEBUG ??????? clnt = clnt_create (host, TESTPROG, VERSION, "udp"); ??????? if (clnt == NULL) { ??????????????? clnt_pcreateerror (host); ??????????????? exit (1); ??????? } #endif? /* DEBUG */ ??????? result_1 = test_1(&test_1_arg, clnt); ??????? if (result_1 == (char **) NULL) { ??????????????? clnt_perror (clnt, "call failed"); ??????? } #ifndef DEBUG ??????? clnt_destroy (clnt); #endif?? /* DEBUG */ } int main (int argc, char *argv[]) { ??????? char *host; ??????? if (argc < 2) { ??????????????? printf ("usage: %s server_host\n", argv[0]); ??????????????? exit (1); ??????? } ??????? host = argv[1]; ??????? testprog_1 (host); exit (0); } |
rpcgen -Ss -o test_srv_func.c test.x |
/* ?* This is sample code generated by rpcgen. ?* These are only templates and you can use them ?* as a guideline for developing your own functions. ?*/ #include "test.h" char ** test_1_svc(char **argp, struct svc_req *rqstp) { ??????? static char * result; ??????? /* ???????? * insert server code here ???????? */ ??????? return &result; } |
至此,我們就可以編譯生成程序來運行了。
用下面的命令編譯生成服務端程序test_server:
gcc -Wall -o test_server test_clnt.c test_srv_func.c test_svc.c |
gcc -Wall -o test_client test_client.c test_clnt.c? |
./test_server |
./test_client 127.0.0.1 |
下面我們先給服務端程序加上代碼,使這個服務器能完成一定的工作。即修改 test_srv_func.c ,在 “ * insert server code here ” 后面加上取時間的代碼,即修改后的 test_srv_func.c 代碼如下:
/* ?* This is sample code generated by rpcgen. ?* These are only templates and you can use them ?* as a guideline for developing your own functions. ?*/ #include <time.h> #include "test.h" char ** test_1_svc(char **argp, struct svc_req *rqstp) { ??????? static char * result; ??????? static char tmp_char[128]; ??????? time_t rawtime; ??????? /* ???????? * insert server code here ???????? */ ??????? if( time(&rawtime) == ((time_t)-1) ) { ??????????????? strcpy(tmp_char, "Error"); ??????????????? result = tmp_char; ??????????????? return &result; ??????? } ??????? sprintf(tmp_char, "服務器當前時間是 :%s", ctime(&rawtime)); ??????? result = tmp_char; ??????? return &result; } |
void testprog_1(char *host) { ??????? CLIENT *clnt; ??????? char * *result_1; ??????? char * test_1_arg; ??????? test_1_arg = (char *)malloc(128); #ifndef DEBUG ??????? clnt = clnt_create (host, TESTPROG, VERSION, "udp"); ??????? if (clnt == NULL) { ??????????????? clnt_pcreateerror (host); ??????????????? exit (1); ??????? } #endif? /* DEBUG */ ??????? result_1 = test_1(&test_1_arg, clnt); ??????? if (result_1 == (char **) NULL) { ??????????????? clnt_perror (clnt, "call failed"); ??????? } ??????? if (strcmp(*result_1, "Error") == 0) { ??????????????? fprintf(stderr, "%s: could not get the time\n", host); ??????????????? exit(1); ??????? } ??????? printf("收到消息 ... %s\n", *result_1); #ifndef DEBUG ??????? clnt_destroy (clnt); #endif?? /* DEBUG */ } |
gcc -Wall -o test_server test_clnt.c test_srv_func.c test_svc.c gcc -Wall -o test_client test_client.c test_clnt.c? |
./test_client 127.0.0.1 收到消息 ... 服務器當前時間是 :Tue Feb 27 11:45:21 2007 |
rpcgen -Sm test.x > Makefile |
# This is a template Makefile generated by rpcgen # Parameters CLIENT = test_client SERVER = test_server SOURCES_CLNT.c =? SOURCES_CLNT.h =? SOURCES_SVC.c =? SOURCES_SVC.h =? SOURCES.x = test.x TARGETS_SVC.c =??????? TARGETS_CLNT.c =??????? TARGETS =???????????? OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o) OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o) # Compiler flags? CFLAGS += -g? LDLIBS += -lnsl RPCGENFLAGS =? # Targets? all : $(CLIENT) $(SERVER) $(TARGETS) : $(SOURCES.x)? ??????? rpcgen $(RPCGENFLAGS) $(SOURCES.x) $(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c)? $(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c)? $(CLIENT) : $(OBJECTS_CLNT)? ??????? $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS)? $(SERVER) : $(OBJECTS_SVC)? ??????? $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS) ?clean: ???????? $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) |
# This is a template Makefile generated by rpcgen # Parameters CLIENT = test_client SERVER = test_server SOURCES_CLNT.c =? SOURCES_CLNT.h =? SOURCES_SVC.c =? SOURCES_SVC.h =? SOURCES.x = test.x TARGETS_SVC.c = test_clnt.c test_srv_func.c test_svc.c TARGETS_CLNT.c = test_clnt.c test_client.c? TARGETS = test.h?? test_clnt.c test_svc.c???? OBJECTS_CLNT = $(SOURCES_CLNT.c:%.c=%.o) $(TARGETS_CLNT.c:%.c=%.o) OBJECTS_SVC = $(SOURCES_SVC.c:%.c=%.o) $(TARGETS_SVC.c:%.c=%.o) # Compiler flags? CFLAGS += -g? LDLIBS += -lnsl RPCGENFLAGS =? # Targets? all : $(CLIENT) $(SERVER) $(TARGETS) : $(SOURCES.x)? ??????? rpcgen $(RPCGENFLAGS) $(SOURCES.x) $(OBJECTS_CLNT) : $(SOURCES_CLNT.c) $(SOURCES_CLNT.h) $(TARGETS_CLNT.c)? $(OBJECTS_SVC) : $(SOURCES_SVC.c) $(SOURCES_SVC.h) $(TARGETS_SVC.c)? $(CLIENT) : $(OBJECTS_CLNT)? ??????? $(LINK.c) -o $(CLIENT) $(OBJECTS_CLNT) $(LDLIBS)? $(SERVER) : $(OBJECTS_SVC)? ??????? $(LINK.c) -o $(SERVER) $(OBJECTS_SVC) $(LDLIBS) clean: ??????? $(RM) core $(TARGETS) $(OBJECTS_CLNT) $(OBJECTS_SVC) $(CLIENT) $(SERVER) *~ |