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


    事前準備:
    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 ", 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 ", host);
                    exit(1);
            }
            printf("收到消息 ... %s ", *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) *~
  • 相关阅读:
    mpvue 引入 vant-weapp 踩坑记录
    mac上hbuilder无法启动微信小程序调试窗口的解决办法
    mac 安装了xcode,flutter doctor 却检测不到展示叉叉
    vue 前端复制粘贴方式上传图片
    401 错误时,几个细节检查
    vue 图片src动态加载
    前端优化的大方向
    how to stop code runner in vscode(macOs)
    window server 2008 r2 TLS 升级1.2
    超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小
  • 原文地址:https://www.cnblogs.com/med-dandelion/p/4532307.html
Copyright © 2020-2023  润新知