RPC
在介绍RPC之前,我们有必要先介绍一下IPC
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。进程是计算机系统分配资源的最小单位。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。这些进程可以运行在同一计算机上或网络连接的不同计算机上。 进程间通信技术包括消息传递、同步、共享内存和远程过程调用。 IPC是一种标准的Unix通信机制。
有两种类型的进程间通信(IPC):
- 本地过程调用(LPC)LPC用在多任务操作系统中,使得同时运行的任务能互相会话。这些任务共享内存空间使任务同步和互相发送信息。
- 远程过程调用(RPC)RPC类似于LPC,只是在网上工作。RPC开始是出现在Sun微系统公司和HP公司的运行UNIX操作系统的计算机中。
为什么要用RPC呢?
我们知道,同一主机的同一程序之间因为有函数栈的存在,使得函数的相互调用很简单。但是两个程序(程序A和程序B)分别运行在不同的主机上,A想要调用B的函数来实现某一功能,那么使用常规的方法就是不可实现的,RPC就是干这个活的
RPC的核心并不在于使用什么协议。RPC的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里。通过RPC能解耦服务,这才是使用RPC的真正目的。RPC的原理主要用到了动态代理模式,至于http协议,只是传输协议而已。简单的实现可以参考spring remoting,复杂的实现可以参考dubbo。
?
总结一下
- RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
- RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯) RPC 是一个请求响应模型。
- 客户端发起请求,服务器返回响应(类似于Http的工作方式) RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
?
XDR
XDR是一个数据描述和数据编码的标准,XDR的主要作用就是在不同进程间传递消息参数时,避免因为计算机平台的不一致而导致数据传送接收异常。它可以对消息参数按照一定的顺序编码,放在一个数据包里(通常是在内存中申请一个一定大小的字符串缓冲区),然后把这个数据包发送给其他平台,然后在按照之前编码的顺序依次解码,并可以获得原来的消息参数。
?
rpcgen
rpcgen介绍
可以看执行man rpcgen
在man page中查看rpcgen的介绍,不得不说man page真的是很强大的工具,里面的介绍说明言简意赅,还能练英语 ??
翻译:
rpcgen是一种工具,它可以生成实现RPC的C语言代码。使用rpcgen时,你需要提供一个与C语言类似的RPC语言源文件。
rpcgen通常通过一个源文件生成四个输出文件。如果输入文件是proto.x,rpcgen将生成一个头文件proto.h,XDR规则proto_xdr.c,服务端存根proto_svc.c,客户端存根proto_clt.c 若使用-T选项,还会生成一个proto_tbl.i,使用-Sc选项,将会生成一个客户端的rpc样例,使用-Ss选项将会生成一个服务端的rpc样例。
rpcgen命令选项
usage: rpcgen infile
rpcgen [-abkCLNTM][-Dname[=value]] [-i size] [-I [-K seconds]] [-Y path] infile
rpcgen [-c | -h | -l | -m | -t | -Sc | -Ss | -Sm] [-o outfile] [infile]
rpcgen [-s nettype]* [-o outfile] [infile]
rpcgen [-n netid]* [-o outfile] [infile]
options:
-a generate all files, including samples
-b backward compatibility mode (generates code for SunOS 4.1)
-c generate XDR routines
-C ANSI C mode
-Dname[=value] define a symbol (same as #define)
-h generate header file
-i size size at which to start generating inline code
-I generate code for inetd support in server (for SunOS 4.1)
-K seconds server exits after K seconds of inactivity
-l generate client side stubs
-L server errors will be printed to syslog
-m generate server side stubs
-M generate MT-safe code
-n netid generate server code that supports named netid
-N supports multiple arguments and call-by-value
-o outfile name of the output file
-s nettype generate server code that supports named nettype
-Sc generate sample client code that uses remote procedures
-Ss generate sample server code that defines remote procedures
-Sm generate makefile template
-t generate RPC dispatch table
-T generate code to support RPC dispatch tables
-Y path directory name to find C preprocessor (cpp)
-5 SysVr4 compatibility mode
--help give this help list
--version print program version
?
部分options解释:
-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),生成后还需要手动添加代码。
?
Linux上编写rpc-demo
Linux下面的RPC模型是SUN RPC (ONC RPC),使用了XDR来编码/解码数据。gcc提供了一些标准数据类型的XDR filter(比如整型,浮点型,字符串等)。对于自定义数据类型,则需要自己编写XDR filter来处理。
你可以使用rpcgen来帮你自动生成xdr filter,但是,该工具需要你提供一个 .x 文件。
这里提供两个demo作为参考
?
- 简易计算器
(1)编写.x文件
/*
* 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程序编号 */
?
(2)使用rpcgen生成文件
想一步到位,生成所有文件的话执行:
$ rpcgen -a calculator.x
(如果你需要分步执行也是可以的,根据上面写的rpcgen命令使用不同的参数)
?
生成以下文件:
文件名 | 作用 |
---|---|
Makefile.calculator | 该文件用于编译所有客户机,服务器代码 |
calculator.h | 声明用到的变量和函数 |
calculator_xdr.c | 非标准数据类型的编码 |
calculator_clnt.c | 将远程调用代理( proxying ) 为本地OS调用,并将调用参数打包成一个消息,然后将此消息发送给服务器。由rpcgen生成,程序员一般无需修改 |
calculator_svc.c | 将通过网络输入的请求转换为本地过程调用,即负责解压服务器上收到的消息,并调用实际的服务器端、应用程序级的实现,程序员一般不用修改 |
calculator_client.c | 骨架客户端程序,需要自行修改 |
calculator_server.c | 骨架服务端程序,需要自行修改 |
?
(3)修改骨架程序
这里需要我们自己修改calculator_client.c和calculator_server.c来实现功能
注:/* -<<< Add to test*/ 包含的为添加的代码
?
calculator_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 "calculator.h"
void
calculator_prog_1(char *host)
{
CLIENT *clnt;
struct CALCULATOR *result_1;
struct CALCULATOR calculator_proc_1_arg;
#ifndef DEBUG
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");
}
#ifndef DEBUG
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);
}
?
calculator_server.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 "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;
}
?
(4)编译
rpcgen帮我们生成了makefile,使用make -f命令调用即可
$ make -f Makefile.calculator
?
(5)执行
?
- 服务端时间获取
(1)编写.x文件
program DATE_PROG {
version DATE_VERS {
long GET_DATE(void) = 1;
} = 1;
} = 0x20000002;
(2)使用rpcgen生成文件
$ rpcgen -a date.x
(3)修改骨架程序
date_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 "date.h"
void
date_prog_1(char *host)
{
CLIENT *clnt;
long *result_1;
char *get_date_1_arg;
#ifndef DEBUG
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*/
#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];
date_prog_1 (host);
exit (0);
}
?
date_server.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 "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;
}
(4)编译(同上一个例子)
(5)执行
?
出现的问题
-
服务器无法启动,错误如下:Cannot register service: RPC: Unable to receive; errno = Connection refused
unable to register (TESTPROG, VERSION, udp).问题原因:系统没有安装 portmap 或者没有启动 portmap 端口映射。
解决方法: 在新版的 ubuntu 中 portmap 被 rpcbind 所代替, 所以需要启动 rpcbind 服务。
service rpcbind restart
?
参考资料
https://www.jianshu.com/p/b0343bfd216e
https://www.jianshu.com/p/b33fc01c1f59
http://blog.chinaunix.net/uid-20644632-id-2220585.html
https://blog.csdn.net/weixin_30538029/article/details/95014067
原文:https://www.cnblogs.com/Irvingcode/p/12887671.html