远程过程调用 Remote Procedure Call
-------------------------------------
通过stub函数,远程的执行过程可以被本地调用
客户端拥有相同的函数接口
Stub 函数的产生
- 使用独立的预编译器rpcgen来使编程语言支持
- 结构定义语言
接口定义语言 Interface Definition Language
type definitions program identifier{ version version_id{ procedure list }=value; ... }=value; |
program PROG{ version PROG1{ void PROC_A(int) = 1; }=1; }=0x3a3afeeb; |
数据类型
struct intpair {int a, b}; |
struct intpair {int a; int b;}; typedef struct intpair intpair; |
数据类型
enum state {BUSY=1, IDLE=2, TRANSIT=3};
union identifier switch (declaration) { case_list } |
union time_s switch (int status) { case 0: char timeval [MAXBUF]; case 1: void; case 2: int reason; } |
数据类型
- 和C相似
- typedef long counter;
- 和C相似,但具有可变长度
long x_vals<50>代表一个最长为50的数组
- 和C相似。在指针指向的数据前有个布尔值,如果指针为null,则布尔值 为FALSE
数据类型
类似声明一个长度可变的数组
string name<50> 一个最长50的字符串 string name<> 一个任意长度的字符串
可以拥有TRUE和FALSE两种值
一组按位顺序存储的没有类型的数据,长度可变。
opaque extra_bytes[512];
稍后声明将会转换为C形式:
struct {
uint more_len; /* 数组长度 */
char *more_val; /* 数据使用的空间 */
};
使用SUN RPC创建例程
- 小写
- 由自定义名字,版本号和"svc"三部分组成,例如BLIP, blip_1_svc
- 可以使用使用结构体传递多个值
- 在较新的版本中rpcgen对这个限制放宽了,但仍是作为默认情况
一个简单的RPC程序
- bin_date返回系统时间,即从1970年第一秒开始的秒数
- str_date把秒数作为输入,返回一个格式化的字符串
- 将bin_date和str_date移动到服务器,通过RPC来调用她们。
Stand-alone程序
#include <stdio.h>
long bin_date(void);
char *str_date(long bintime);
main(int argc, char **argv) {
long lresult; /* 从bin_date()返回的值 */
char *sresult; /* 从str_date()返回的值 */
if (argc != 1) {
fprintf(stderr, "usage: %s/n", argv[0]);
exit(1);
}
/* 调用bin_date()过程 */
lresult = bin_date();
printf("time is %ld/n", lresult);
/* 格式化输出结果 */
sresult = str_date(lresult);
printf("date is %s", sresult);
exit(0);
}
Stand-alone程序:函数
/* bin_date 以二进制的形式返回系统时间 */
long bin_date(void) {
long timeval;
long time(); /* 返回时间 */
timeval = time((long *)0);
return timeval;
}
/* str_date 转换时间从二进制到格式化字符串 */
char *str_date(long bintime) {
char *ptr;
char *ctime();
ptr = ctime(&bintime);
return ptr;
}
定义远程接口(IDL)
- bin_date()没有参数,返回一个long型结果
- str_date()接受一个long型作为输入,然后返回字符串
program DATE_PROG {
version DATE_VERS {
long BIN_DATE(void) = 1;
string STR_DATE(long) = 2
} = 1;
} = 0x31423456;
产生服务器函数:rpcgen的模板
rpcgen -C -Ss date.x >server.c
#include "date.h"
long *
bin_date_1_svc(void *argp, struct svc_req *rqstp)
{
static long result;
/* insert server code here */
return &result;
}
char **
str_date_1_svc(long *argp, struct svc_req *rqstp)
{
static char *result;
/* insert server code here */
return &result;
}
产生服务器函数:插入代码
long *
bin_date_1_svc(void *argp, struct svc_req *rqstp)
{
static long result;
long time();
result = time((long *)0);
return &result;
}
char **
str_date_1_svc(long *bintime, struct svc_req *rqstp)
{
static char *result;
char *ctime();
result = ctime(bintime);
return &result;
}
产生客户端函数:获得服务器名字
- 使用getopt库函数从命令行接收的-h hostname参数。
extern char *optarg;
extern int optind;
char *server = "localhost"; /* default */
int err = 0;
while ((c = getopt(argc, argv, "h:")) != -1)
switch (c) {
case 'h':
server = optarg;
break;
case '?':
err = 1;
break;
}
/* exit if error or extra arguments */
if (err || (optind < argc)) {
fprintf(stderr, "usage: %s [-h hostname]/n", argv[0]);
exit(1);
}
产生客户端函数:添加头文件和创建客户端句柄
#include <rpc/rpc.h>
#include "date.h"
CLIENT *c1;
c1 = clnt_create(server, DATE_PROG, DATE_VERS, "netpath");
产生客户端函数:修改远程函数的调用
- 给函数添加版本号
- 添加一个客户端的句柄作为参数(由clnt_create返回)
- 经常传递单个参数(如果没有的话就传递NULL)
bin_date_1(NULL, c1);
str_date_1(&value, c1);
产生客户端函数:检查RPC错误
long *lresult;
if ((lresult = bin_date_1(NULL, c1)) == NULL){
clnt_perror(c1, server);
exit(1);
}
产生客户端函数:检查RPC错误(2)
char **sresult;
if((sresult = str_date_1(lresult, c1)) == NULL)
{
clnt_perror(cl, server);
exit(1);
}
printf("date is %s", *sresult);
编译-连接-运行
生成stubs
rpcgen -C date.x
编译连接客户端和客户端stub
cc -o client client.c date_clnt.c -lnsl
编译连接服务器和服务器stub
cc -o server -DRPC_SVC_FG server.c date_svc.c -lnsl
$ ./server
$ ./client -h remus
time on localhost is 970457832
date is Sun Oct 1 23:37:12 2000