远程调用-Sun RPC
一、概述
构建一个应用程序时,应该在以下两者之间作出选择:
- 构建一个庞大的单一程序,完成全部工作。
- 把整个应用程序散布到彼此通信的多个进程中。
选择2,需做如下选择:
- 假设所有进程运行在同一台主机上。
- 假设某些进程会运行在其他主机上。
不同部分之间需要网络通信的应用程序大多数是使用显式网络编程方式编写的,也就是如UNPvl中讲述的那样直接调用套接字API或 XTI API
。使用套接字API时,客户调用socket、connect、read和write
, 服务器则调用socket、bind、listen、 accept、read和write
。熟悉的大多数应用程序(Web浏览器、Web服务器、Telne客户、 Telnet服务器等程序)就是以这种方式编写的。
编写分布式应用程序的另一种方式是使用隐式网络编程远程过程调用(RPC)提供了这样的一个工具。使用早已熟悉的过程调用来编写应用程序,但是调用进程(客户)和含有被调用过程的进程(服务器)可在不同的主机上执行。
客户和服务器运行在不同的主机上而且过程调用中涉及网络I/O, 这样的事实对于程序员基本上是透明的。事实上衡量 一 个RPC 软件包的测度之一就是它能使作为底层支撑的网络I/O对程序员的透明度有多大。
二、函数clnt_create
此函数运行成功返回一个客户句柄。
#include<rpc/rpc.h>
CLIENT *clnt_create(const char *host,unsigned long program,unsigned long versnum,const char *protocol);
//host:主机名、IP地址
//program:程序名
//versnum:版本号
//program和versnum来自square.x文件
//protocol:协议选择,指定为TCP或UDP
三、RPC例子
例:客户以一个长整数调用服务器过程,服务器返回该值的平方。
文件square.x(.x
结尾的文件称为RPC说明书文件,定义了服务器过程以及这些过程的参数和结果)内容:
struct square_in {
/* 输入(参数) */
long arg1;
};
struct square_out {
/* 输出(结果) */
long res1;
};
program SQUARE_PROG {
version SQUARE_VERS {
square_out SQUAREPROC(square_in) = 1; /* 程序编号= 1 */
} = 1; /* 版本号 */
} = 0x31230000; /*程序编号 */
使用随Sun RPC软件包提供的程序来编写这个说明书文件,程序是rpc_gen
。
#include "square.h"//由rpcgen产生的square.h
int main(int argc, char **argv)
{
//声明客户句柄
CLIENT *cl;
square_in in;
square_out *outp;
if (argc != 3)
err_quit("usage: client <hostname> <integer-value>");
//获取客户句柄
cl = clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, "tcp");
//调用远程过程并输出结果
in.arg1 = atol(argv[2]);
if ( (outp = squareproc_1(&in, cl)) == NULL)
err_quit("%s", clnt_sperror(cl, argv[1]));
printf("result: %ld\n", outp->res1);
exit(0);
}
square.x说明文件中,称远程为SQUAREPROC
,客户程序中称squareproc_1
。约定:.x
文件中的名字转换成小写字母形式,添上一个底划线后跟版本号。
服务器程序:只编写过程,服务器程序的main函数由rpc_gen
程序自动生成。
#include "square.h"
//过程参数
square_out * squareproc_1_svc(square_in *inp, struct svc_req *rqstp)
{
static square_out out;
out.res1 = inp->arg1 * inp->arg1;
return(&out);
}
此客户-服务器程序没有使用任何显示的网络编程方式。涉及的套接字以及网络I/O的所有细节都由RPC运行时系统来处理。这就是RPC目的:不需要显示的网络编程知识就允许编写分布式应用程序。
构建客户程序可执行文件的步骤:
rpcgen 的-C
表示在square.h头文件在生成ANSI C原型。rpcgen
还产生一个称为客户程序存根的源文件square_clnt.c
和一个名为square_xdr.c
的用来处理XDR数据转换文件。libunpipc.a
是函数库,-lnsl
选项指定存放网络支撑函数的系统函数库。
构建服务器程序可执行文件的步骤:
文件square_svc.c
中含有服务器程序main
函数,构建客户程序生成的含有XDR函数的square_xdr.o
文件在服务器程序的构建也需要。
下图显示构建客户-服务器程序所需的文件和步骤。阴影方框必须编写的文件。短划线指出来需要C伪指令#include square.h
的那些文件。
下图汇总了一次远程过程调用中通常发生的步骤。
-
服务器启动,向所在主机上的端口映射器注册自身。然后客户启动调用
clent_create
,与服务器主机上的端口映射器联系,找到服务器的临时端口。clent_create
函数还建立一个与服务器的TCP连接。 -
客户调用一个称为客户程序存根的本地过程(过程名squareproc_1,含有客户程序存根(相要调用真正的服务器过程)的文件由rpcgen产生,名为square_clnt.c)。存根目的在于把有待传递给远程过程的参数打包,转换成某种标准格式,然后构造一个或多个网络消息(集结)。客户程序的各个例程和存根通常调用RPC运行函数库中的函数。
-
这些网络消息由客户程序存根发送给远程系统,需要一次陷入本地内核的系统调用(write或sendto)。
-
这些网络消息传送到远程系统。运用网络协议TCP或UDP。
<