c++小牛钱小白

c中linux中进程间通信IPC之远程过程调用RPC在实战中的应用??

RPC:Remote Procedure Call 远程过程调用

RPC的作用

       同一主机的同一程序之间因为有函数栈的存在,使得函数的相互调用很简单。但是两个程序(程序A和程序B)分别运行在不同的主机上,A想要调用B的函数来实现某一功能,那么使用常规的方法就是不可实现的,就需要使用RPC。

       RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务,这才是使用RPC的真正目的。RPC的原理主要用到了动态代理模式,至于http协议,只是传输协议而已。简单的实现可以参考spring remoting,复杂的实现可以参考dubbo。

       RPC会隐藏底层的通信细节(不需要直接处理Socket通信或Http通信),RPC是一个请求响应模型。

XDR

       XDR是一个数据描述和数据编码的标准,XDR的主要作用就是在不同进程间传递消息参数时,避免因为计算机平台的不一致而导致数据传送接收异常。它可以对消息参数按照一定的顺序编码,放在一个数据包里(通常是在内存中申请一个一定大小的字符串缓冲区),然后把这个数据包发送给其他平台,然后在按照之前编码的顺序依次解码,并可以获得原来的消息参数。

rpcgen命令选项

-a 生成所有源程序,包括客户机和服务器源程序。

-C 使用ANSI C标准生成编码。

-c 生成xdr转码C程序。(file_xdr.c)

-l 生成客户机stubs。(file_clnt.c)

-m 生成服务器stubs,但是不生成main函数。(file_svc.c)

-s  rpcgen –C –s tcp file.x,生成服务器stubs,用tcp协议,同时生成了main函数。(file_svc.c)

-h 生成头文件。

-Sc 生成骨架客户机程序,(file_client.c),生成后还需要手动添加代码。

-Ss 生成服务器程序,(file_server.c),生成后还需要手动添加代码。

-Sm 生成Makefile文件,例:rpcgen -Sm test.x > Makefile。

RPC案例

       Linux下面的RPC模型是SUN RPC (ONC RPC),使用了XDR来编码/解码数据。gcc提供了一些标准数据类型的XDR filter(比如整型,浮点型,字符串等)。对于自定义数据类型,则需要自己编写XDR filter来处理。可以使用rpcgen来帮助自动生成xdr filter,但是,该工具需要提供一个 .x 文件。

代码说明

cat>>calculator.x<<EOF

/* 

 * filename: calculator.x

 * function: 定义远程调用中常量、非标准数据类型以及调用过程

 */


const ADD = 0;

const SUB = 1;

const MUL = 2;

const DIV = 3;


struct CALCULATOR

{

    int op; /* 0-ADD, 1-SUB, 2-MUL, 3-DIV */

    float arg1;

    float arg2;

    float result;

};


program CALCULATOR_PROG

{

    version CALCULATOR_VER

    {

        struct CALCULATOR CALCULATOR_PROC(struct CALCULATOR) = 1;

    } = 1;  /*  版本号=1 */

} = 0x20000001; /* RPC程序编号 */

EOF

rpcgen -a calculator.x;# 生成所有文件

#rpcgen calculator.x;


cat>calculator_client.c<<EOF

/*

 * 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 "calculator.h"


void

calculator_prog_1(char *host)

{

CLIENT *clnt;

struct CALCULATOR  *result_1;

struct CALCULATOR  calculator_proc_1_arg;


#ifndefDEBUG

clnt = clnt_create (host, CALCULATOR_PROG, CALCULATOR_VER, "udp");

if (clnt == NULL) {

clnt_pcreateerror (host);

exit (1);

}

#endif/* DEBUG */


/* -<<< Add to test*/

    char c;


    printf("choose the operation:\n\t0---ADD\n\t1---SUB\n\t2---MUL\n\t3---DIV\n");

    c = getchar();


if(c>'3' && c<'0'){

printf("error:operate/n");

exit(1);

}

    calculator_proc_1_arg.op = c-'0';


    printf("input the first number:");

    scanf("%f", &calculator_proc_1_arg.arg1);


    printf("input the second number:");

    scanf("%f", &calculator_proc_1_arg.arg2);


    /* -<<< Add to test*/

result_1 = calculator_proc_1(&calculator_proc_1_arg, clnt);

if (result_1 == (struct CALCULATOR *) NULL) {

clnt_perror (clnt, "call failed");

}

#ifndefDEBUG

clnt_destroy (clnt);

#endif /* DEBUG */


    /* -<<< Add to test*/

    printf("The Result is %.3f \n", result_1->result);

    /* -<<< Add to test*/

}


int

main (int argc, char *argv[])

{

char *host;


if (argc < 2) {

printf ("usage: %s server_host\n", argv[0]);

exit (1);

}

host = argv[1];

calculator_prog_1 (host);

exit(0);

}

EOF

cat>calculator_server.c<<EOF

/*

 * 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 "calculator.h"


struct CALCULATOR *

calculator_proc_1_svc(struct CALCULATOR *argp, struct svc_req *rqstp)

{

static struct CALCULATOR  result;


/*

 * insert server code here

 */

 /* -<<< Add to test*/

    switch(argp->op){

case ADD:

            result.result = argp->arg1 + argp->arg2;

            break;

        case SUB:

            result.result = argp->arg1 - argp->arg2;

            break;

        case MUL:

            result.result = argp->arg1 * argp->arg2;

            break;

        case DIV:

            result.result = argp->arg1 / argp->arg2;

            break;

        default:

            break;

    }

    /* -<<< Add to test*/

return &result;

}

EOF

#安装portmap

sudo apt-get install portmap;

sudo service portmap start;

#启动rpcbind

service rpcbind restart

#编译,运行make -f命令

make -f Makefile.calculator;

./calculator_server;

./calculator_client 127.0.0.1;

#运行结果,报错:unable to register (CALCULATOR_PROG, CALCULATOR_VER, udp).127.0.0.1: RPC: Port mapper failure - RPC: Unable to send??

问题原因:系统没有安装 portmap 或者没有启动 portmap 端口映射。

解决方法: 在新版的 ubuntu 中 portmap 被 rpcbind 所代替, 所以需要启动 rpcbind 服务。

service rpcbind restart

注:rpcgen -a calculator.x命令生成的文件说明

    文件                                    作用

Makefile.calculator    该文件用于编译所有客户机、服务器代码

calculator.h                声明用到的变量和函数

calculator_xdr.c          非标准数据类型的编码

calculator_clnt.c         将远程调用代理( proxying )为本地OS调用,

                                   并将调用参数打包成一个消息,然后将此消息

                                   发送给服务器。由rpcgen生成,程序员一般无

                                   需修改

calculator_svc.c          将通过网络输入的请求转换为本地过程调用,

                                   即负责解压服务器上收到的消息,并调用实际

                                   的服务器端(应用程序级的实现),程序员一般

                                   不用修改

calculator_client.c      骨架客户端程序,需要自行修改

calculator_server.c     骨架服务端程序,需要自行修改

RPC案例2

代码说明

//服务器获取时间

# 编写.x文件

cat>>date.x<<EOF

program DATE_PROG { 

    version DATE_VERS { 

        long GET_DATE(void) = 1; 

    } = 1; 

} = 0x20000002;

EOF

# 使用rpcgen生成文件

rpcgen -a date.x;

# 修改骨架程序

# date_client.c

cat>date_client.c<<EOF

/*

 * 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 "date.h"


void

date_prog_1(char *host)

{

CLIENT *clnt;

long  *result_1;

char *get_date_1_arg;


#ifndefDEBUG

clnt = clnt_create (host, DATE_PROG, DATE_VERS, "udp");

if (clnt == NULL) {

clnt_pcreateerror (host);

exit (1);

}

#endif/* DEBUG */


result_1 = get_date_1((void*)&get_date_1_arg, clnt);

if (result_1 == (long *) NULL) {

clnt_perror (clnt, "call failed");

}


 /* -<<< Add to test*/

printf("%ld\n",*result_1);

 /* -<<< Add to test*/

 

#ifndefDEBUG

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];

date_prog_1 (host);

exit (0);

}

EOF

# date_server.c

cat>date_server.c<<EOF

/*

 * 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 "date.h"

#include <time.h>


long *

get_date_1_svc(void *argp, struct svc_req *rqstp)

{

static long  result;


/*

 * insert server code here

 */


 /* -<<< Add to test*/

result = (long)time(0);

 /* -<<< Add to test*/

return &result;

}

EOF

# 编译:rpcgen帮我们生成了makefile,使用make -f命令调用即可

make -f Makefile.date;

./date_server

./date_client 127.0.0.1

# 运行出错;错误:unable to register (DATE_PROG, DATE_VERS, udp).127.0.0.1: RPC: Port mapper failure - RPC: Unable to send

ls -lh;

注:参考:https://www.cnblogs.com/bluettt/p/12887671.html

评论
© c++小牛钱小白 | Powered by LOFTER