您的位置:首页 > 运维架构 > Linux

我是这样学习Linux下C语言编程的-RPC远程调用编程

2014-05-19 14:33 495 查看
事前準備:

a.安裝rpcgen

#apt-get install  libc-dev-bin

b.安裝c compiler

# apt-get install  build-essential

在查看libc6-dev软件包提供的工具(用 dpkg -L libc6-dev 命令)的时候,发现此软件包提供了一个有用的工具rpcgen命令。通过rpcgen的man手册看到此工具的作用是把RPC源程序编译成C语言源程序,从而轻松实现远程过程调用。

下面的例子程序的作用是客户端程序取中心服务器上时间的,编程过程如下:

先编写一个 “ RPC 语言 ” ( RPC Language ( Remote Procedure Call Language ) ) 的源文件 test.x ,文件后缀名为 x 。

源代码如下:

下面的例子程序的作用是客户端程序取中心服务器上时间的,编程过程如下:

先编写一个 “ 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->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
源代码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;  //这里需要手动改为char * test_1_arg="",否则会报错误:call failed: RPC: Can't encode arguments  

#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

收到消息 ... 服务器当前时间是 :Tue Feb 27 11:45:21 2007
为了省略每次输入gcc命令的麻烦,也为了维护我们的工程,可以运行下列命令生成一个Makefile文件:
rpcgen -Sm test.x > Makefile
生成的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)
由于我们手工生成了源文件,所以要修改一下这个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 = 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) *~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: