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