Linux下C語言編程的-RPC遠程調用編程

http://zhoulifa.bokee.com/6128714.html

 

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

#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) *~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章