Programming 版 (精华区)

发信人: lofe (〖感激生活Θ路无边〗), 信区: Programming
标  题: 远程过程调用(RPC)编程简介 
发信站: 哈工大紫丁香 (2002年03月07日12:26:07 星期四), 站内信件

远程过程调用(RPC)编程简介  

sailor  (发表时间:2001-12-31 9:11:46  点击次数:141)  

一、概述 
在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的
一段代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在
网络日益发展的今天已无法适应实际需求。总而言之,传统过程调用模式无法充分
利用网络上其他主机的资源(如CPU、Memory等),也无法提高代码在实体间的共
享程度,使得主机资源大量浪费。 
而本文要介绍的RPC编程,正是很好地解决了传统过程所存在的一系列弊端。通过
RPC我们可以充分利用非共享内存的多处理器环境(例如通过局域网连接的多台工
作站),这样可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一
个多处理器的计算机上一样。你可以方便的实现过程代码共享,提高系统资源的利
用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻
前端机的负担。 
二、RPC的结构原理及其调用机制 
如前所述RPC其实也是一种C/S的编程模式,有点类似C/S Socket 编程模式,但要
比它更高一层。当我们在建立RPC服务以后,客户端的调用参数通过底层的RPC传输
通道,可以是UDP,也可以是TCP(也即TI-RPC—无关性传输),并根据传输前所提
供的目的地址及RPC上层应用程序号转至相应的RPC Application Porgramme 
Server ,且此时的客户端处于等待状态,直至收到应答或Time Out超时信号。具
体的流程图如图1。当服务器端获得了请求消息,则会根据注册RPC时告诉RPC系统
的例程入口地址,执行相应的操作,并将结果返回至客户端。 

当一次RPC调用结束后,相应线程发送相应的信号,客户端程序才会继续运行。 
当然,一台服务主机上可以有多个远程过程提供服务,那么如何来表示一个唯一存
在的远程过程呢?一个远程过程是有三个要素来唯一确定的:程序号、版本号和过
程号。程序号是用来区别一组相关的并且具有唯一过程号的远程过程。一个程序可
以有一个或几个不同的版本,而每个版本的程序都包含一系列能被远程调用的过程
,通过版本的引入,使得不同版本下的RPC能同时提供服务。每个版本都包含有许
多可供远程调用的过程,每个过程则有其唯一标示的过程号。 
三、基于RPC的应用系统开发 
通过以上对RPC原理的简介后,我们再来继续讨论如何来开发基于RPC的应用系统。
一般而言在开发RPC时,我们通常分为三个步骤: 
a、定义说明客户/服务器的通信协议。 
这里所说的通信协议是指定义服务过程的名称、调用参数的数据类型和返回参数的
数据类型,还包括底层传输类型(可以是UDP或TCP),当然也可以由RPC底层函数
自动选择连接类型建立TI-RPC。最简单的协议生成的方法是采用协议编译工具,常
用的有Rpcgen,我会在后面实例中详细描述其使用方法。 
b、开发客户端程序。 
c、开发服务器端程序。 
开发客户端和服务器端的程序时,RPC提供了我们不同层次的开发例程调用接口。
不同层次的接口提供了对RPC不同程度控制。一般可分为5个等级的编程接口,接下
来我们分别讨论一下各层所提供的功能函数。 
1、简单层例程 
简单层是面向普通RPC应用,为了快速开发RPC应用服务而设计的,他提供了如下功
能函数。 
 函数名 
       功能描述 

Rpc_reg( ) 
在一特定类型的传输层上注册某个过程,来作为提供服务的RPC程序 

Rpc_call( ) 
远程调用在指定主机上指定的过程 

Rpc_Broadcast( ) 
向指定类型的所有传输端口上广播一个远程过程调用请求 


2、高层例程 
在这一层,程序需要在发出调用请求前先创建一个客户端句柄,或是在侦听请求前
先建立一个服务器端句柄。程序在该层可以自由的将自己的应用绑在所有的传输端
口上,它提供了如下功能函数。 
 函数名 
       功能描述 

Clnt_create( ) 
程序通过这个功能调用,告诉底层RPC服务器的位置及其传输类型 

Clnt_create_timed( ) 
定义每次尝试连接的超时最大时间 

Svc_create( ) 
在指定类型的传输端口上建立服务器句柄,告诉底层RPC事件过程的相应入口地址
 

Clnt_call() 
向服务器端发出一个RPC调用请求 


3、中间层例程 
中间层向程序提供更为详细的RPC控制接口,而这一层的代码变得更为复杂,但运行
也更为有效,它提供了如下功能函数。 
 函数名 
       功能描述 

Clnt_tp_create( ) 
在指定的传输端口上建立客户端句柄 

Clnt_tp_create_timed( ) 
定义最大传输时延 

Svc_tp_creaet( ) 
在指定的传输端口上建立服务句柄 

Clnt_call( ) 
向服务器端发出RPC调用请求 


4、专家层例程 
这层提供了更多的一系列与传输相关的功能调用,它提供了如下功能函数。 
 函数名 
       功能描述 

Clnt_tli_create( ) 
在指定的传输端口上建立客户端句柄 

Svc_tli_create( ) 
在指定的传输端口上建立服务句柄 

Rpcb_set( ) 
通过调用rpcbind将RPC服务和网络地址做映射 

Rpcb_unset( ) 
删除rpcb_set( ) 所建的映射关系 

Rpcb_getaddr( ) 
调用rpcbind来犯会指定RPC服务所对应的传输地址 

Svc_reg( ) 
将指定的程序和版本号与相应的时间例程建起关联 

Svc_ureg( ) 
删除有svc_reg( ) 所建的关联 

Clnt_call( ) 
客户端向指定的服务器端发起RPC请求 


5、底层例程 
该层提供了所有对传输选项进行控制的调用接口,它提供了如下功能函数。 
 函数名 
       功能描述 

Clnt_dg_create( ) 
采用无连接方式向远程过程在客户端建立客户句柄 

Svc_dg_create( ) 
采用无连接方式建立服务句柄 

Clnt_vc_create( ) 
采用面向连接的方式建立客户句柄 

Svc_vc_create( ) 
采用面向连接的方式建立RPC服务句柄 

Clnt_call( ) 
客户端向服务器端发送调用请求 


四、 实例介绍 
以下我将通过实例向读者介绍通过简单层RPC的实现方法。通常在此过程中我们将
使用RPC协议编译工具—Rpcgen。Rpcgen 工具用来生成远程程序接口模块,它将以
RPC语言书写的源代码进行编译,Rpc 语言在结构和语法上同C语言相似。由Rpcgen
 编译生成的C源程序可以直接用C编译器进行编译,因此整个编译工作将分为两个
部分。Rpcgen的源程序以.x结尾,通过其编译将生成如下文件: 
a) 一个头文件(.h)包括服务器和客户端程序变量、常量、类型等说明。 
b) 一系列的XDR例程,它可以对头文件中定义的数据类型进行处理。 
c) 一个Server 端的标准程序框架。 
d) 一个Client 端的标准程序框架。 
当然,这些输出可以是选择性的,Rpcgen 的编译选项说明如下: 
 选项 
功能 

'-' a 
生成所有的模板文件 

'-' Sc 
生成客户端的模板文件 

'-' Ss 
生成服务器端的模板文件 

'-' Sm 
生成Makefile 文件 


(详见Solaris Rpcgen Manaul) 
Rpcgen 源程序 time.x: 
/* time.x: Remote time printing protocol */ 
program TIMEPROG { 
version PRINTIMEVERS { 
string PRINTIME(string) = 1; 
} = 1; 
} = 0x20000001; 
time_proc.c源程序: 
/* time_proc.c: implementation of the remote procedure "printime" */ 
#include  
#include /* always needed */ 
#include "time.h" /* time.h will be generated by rpcgen */ 
#include  
/* Remote version of "printime" */ 
char ** printime_1(char **msg,struct svc_req *req) 


static char * result; /* must be static! */ 
static char tmp_char[100]; 
time_t rawtime; 

FILE *f; 

f = fopen("/tmp/rpc_result", "a+"); 
if (f == (FILE *)NULL) { 
strcpy(tmp_char,"Error"); 
result = tmp_char;; 
return (&result); 

fprintf(f, "%s\n", *msg); //used for debugging 
fclose(f); 
time(&rawtime); 
sprintf(tmp_char,"Current time is :%s",ctime(&rawtime)); 
result =tmp_char; 
return (&result); 

rtime.c源代码 
/* 
* rtime.c: remote version 
* of "printime.c" 
*/ 

#include 
#include "time.h" /* time.h generated by rpcgen */ 

main(int argc, char **argv) 


CLIENT *clnt; 
char *result; 
char *server; 
char *message; 


if (argc != 3) { 
fprintf(stderr, "usage: %s host message\n", argv[0]); 
exit(1); 


server = argv[1]; 
message = argv[2]; 

/* 
* Create client "handle" used for 
* calling TIMEPROG on the server 
* designated on the command line. 
*/ 

clnt = clnt_create(server, TIMEPROG, PRINTIMEVERS, "visible"); 

if (clnt == (CLIENT *)NULL) { 
/* 
* Couldn't establish connection 
* with server. 
* Print error message and die. 
*/ 

clnt_pcreateerror(server); 
exit(1); 


/* 

* Call the remote procedure 
* "printime" on the server 
*/ 

result =*printime_1(&message,clnt); 
if (result== (char *)NULL) { 
/* 
* An error occurred while calling 
* the server. 
* Print error message and die. 
*/ 

clnt_perror(clnt, server); 
exit(1); 


/* Okay, we successfully called 
* the remote procedure. 
*/ 

if (strcmp(result,”Error”) == 0) { 

/* 
* Server was unable to print 
* the time. 
* Print error message and die. 
*/ 

fprintf(stderr, "%s: could not get the time\n",argv[0]); 
exit(1); 

printf("From the Time Server ...%s\n",result); 
clnt_destroy( clnt ); 
exit(0); 

有了以上的三段代码后,就可用rpcgen 编译工具进行RPC协议编译,命令如下: 

$rpcgen time.x 
rpcgen 会自动生成time.h、time_svc.c、time_clnt.c 
再用系统提供的gcc进行C的编译,命令如下: 
$gcc rtime.c time_clnt.c -o rtime –lnsl //客户端编译 
$gcc time_proc.c time_svc.c -o time_server –lnsl //服务器端编译 
编译成功后即可在Server端运行time_server,立即将该服务绑定在rpc服务端口上
提供服务。在客户端运行./rtime hostname msg (msg 是一字符串,笔者用来测试
时建立的),立即会返回hostname 端的时间。 
由于,在Sun Solaris 中无法获取远端Server 上时钟信息的功能(不改变本地
Server时钟),笔者曾将此程序应用于计费服务器同时钟服务器同步监测的网管系
统中,运行稳定,获得了较好的效果。应该说RPC的应用是十分广泛的,特别是在
分布式计算领域中尤为显得重要。当然,笔者也是刚接触RPC,还有很多地方了解
的不够深刻,望广大读者多指教。 
 

--
十年生死两茫茫,不思量,自难忘。千里孤坟,无处话凄凉。
纵使相逢应不识,尘满面,鬓如霜。 

夜来幽梦忽还乡。小轩窗,正梳妆。相顾无言,惟有泪千行。
料得年年断肠处,明月夜,短松冈。
 

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 天外飞仙]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.786毫秒