标签:
在查看libc6-dev软件包提供的工具(用 dpkg -L libc6-dev 命令)的时候,发现此软件包提供了一个有用的工具rpcgen命令。
通过rpcgen的man手册看到此工具的作用是把RPC源程序编译成C语言源程序,从而轻松实现远程过程调用。
下面的例子程序的作用是客户端程序取中心服务器上时间的,编程过程如下:
先编写一个 “ RPC 语言 ” ( RPC Language ( Remote Procedure Call Language ) ) 的源文件 test.x ,文件后缀名为 x 。
源代码如下:
program TESTPROG { version VERSION { string TEST(string) = 1; } = 1; } = 87654321;
说明:这里数字87654321是RPC程序编号,还有VERSION版本号为1,都是给RPC服务程序用的。同时指定程序接受一个字符串参数。
运行这个命令:
rpcgen test.x |
将生成三个源文件:
test_clnt.c test.h test_svc.c |
源文件test_clnt.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- 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
源代码test_client.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 "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); }
运行这个命令生成服务端源文件test_srv_func.c:
rpcgen -Ss -o test_srv_func.c test.x |
源文件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 "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 |
用下面的命令编译生成客户端程序test_client:
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; }
再修改客户端代码以显示服务器端返回的内容,即修改test_client.c源文件,只需要修改其中的函数testprog_1,修改后如下:
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
最后对于初学者来说有一个非常非常重要的事情!很多人可能照着上述过程生成可执行文件后在运行服务器时会出现这样的问题:1.服务器无法启动,错误如下:
Cannot register service: RPC: Unable to receive; errno = Connection refused
unable to register (TESTPROG, VERSION, udp).
原因是没有启动portmap端口映射!
原因是没有启动portmap端口映射!
原因是没有启动portmap端口映射!
新版的portmap被rpcbind取代,一定要先安装rpcbind。
好了,经历半天的努力终于把所有环境都搭好,跑出程序来了,开心!
[转]Linux下C语言-RPC远程调用编程rpcgen用法
标签:
原文地址:http://www.cnblogs.com/GaryGu/p/4975782.html