[转]Linux下C语言-RPC远程调用编程rpcgen用法

在查看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。

好了,经历半天的努力终于把所有环境都搭好,跑出程序来了,开心!

时间: 11-16

[转]Linux下C语言-RPC远程调用编程rpcgen用法的相关文章

LINUX下C语言编程基础

实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用gdb调试技术 6. 熟悉makefile基本原理及语法规范 7. 掌握静态库和动态库的生成 二.实验步骤 1. 快捷键 Ubuntu中: 2. vim VIM是一个非常好的文本编辑器,很多专业程序员使用VIM编辑代码,即使以后你不编写程序,只要跟文本打交道,都应该学学VIM,可以浏览参考一下普通人

测试JSON RPC远程调用(JSON客户端)

#include <string> #include <iostream> #include <curl/curl.h> /* 标题:JSon客户端 Author: Kagula LastUpdateDate:2014-05-17 描述:测试JSON RPC远程调用 测试环境:Windows 8.1.Visual Studio 2013 SP1 curl-7.36.0 CPPCMS 1.0.4(JSON服务端) Java Servlet (JSON服务端) */ sta

Linux下C语言使用openssl库进行加密

在这里插一小节加密的吧,使用openssl库进行加密. 使用MD5加密 我们以一个字符串为例,新建一个文件filename.txt,在文件内写入hello ,然后在Linux下可以使用命令md5sum filename.txt计算md5值 ==> b1946ac92492d2347c6235b4d2611184  .虽然写入的是hello这5个字符,但是我们使用命令xxd filename.txt后可以看出文件结尾处会有个0x0a这个回车符.所以在下面的代码中才会有\n. 1 //打开/usr/

笔记整理——Linux下C语言正则表达式

Linux下C语言正则表达式使用详解 - Google Chrome (2013/5/2 16:40:37) Linux下C语言正则表达式使用详解 2012年6月6日Neal627 views发表评论阅读评论 标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库. C语言处理正则表达式常用的函数有regcomp

測试JSON RPC远程调用(JSONclient)

#include <string> #include <iostream> #include <curl/curl.h> /* 标题:JSonclient Author: Kagula LastUpdateDate:2014-05-17 描写叙述:測试JSON RPC远程调用 測试环境:Windows 8.1.Visual Studio 2013 SP1 curl-7.36.0 CPPCMS 1.0.4(JSON服务端) Java Servlet (JSON服务端) *

UNIX/Linux下C语言的学习路线

一.工具篇 “公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工具. 1.操作系统    在UNIX或Linux系统中学习C很方便,所以在开始您的学习旅程前请先选择一个UNIX或Linux操作系统,目前可供个人免费使用的UNIX或Linux系统有FreeBSD.RedHat Linux.SUSE Linux等,而且在安装包中还提供很多实用的工具,如:gcc, make等. 如果您一直使用Window

Linux下C语言执行过程(预处理,编译,汇编,链接,执行)

1.C语言的执行过程包括5个步骤:分别是:预处理,编译,汇编,链接,执行 第一步:编写C源代码,截图如下: 2.预处理,命令为:gcc -E variable.c -o variable.i(这步的作用是文件的展开和宏替换),生成的文件类型是.i类型的. 3.编译:命令为:gcc -S variable.i -o variable.s,这里的.s文件就成了会变语言,截图如下: 4.汇编,命令是:gcc -c variable.s -o variable.o,截图如下: 5,链接:命令:gcc -

[计算机网络] Linux下使用corkscrew进行远程连接

最近重装了电脑,使用ssh远程连接机房的机器时,发生连接超时,再ping机房的机器,没有回应,也就是说我的电脑和机房的电脑不是连通的,再ping机房的网关,却可以ping通.于是,就需要找一种通过代理来连接机房的方法:corkscrew. 1 安装corkscrew sudo apt-get install corkscrew 2 配置corkscrew 在用户主目录下新建.corkscrew-auth,将远程机器的用户名和密码以下面的格式放在里面: username:passwd 在用户主目录

Linux下C语言的几道经典面试题

本篇文章整理了几道Linux下C语言的经典面试题,相信对大家更好的理解Linux下的C语言会有很大的帮助,欢迎大家探讨指正. 1.如果在Linux下使用GCC编译器执行下列程序,输出结果是什么? 答案如下: 2.C语言程序不使用任何条件运算符,打印出十次"Hello"? 答案如下: 或是: 3.如果在Linux下使用GCC编译器执行下列程序,输出结果是什么? 答案如下: 4.如果在Linux下使用GCC编译器执行下列程序,输出结果是什么? 答案如下: 5.如果在Linux下使用GCC编