RPC编程指导

使用RPC编程

 

远程过程调用 Remote  Procedure Call

-------------------------------------

通过stub函数,远程的执行过程可以被本地调用

客户端拥有相同的函数接口

  • - 和服务器通信的接口
  • - 获得服务器执行结果的接口

 

Stub 函数的产生

  • 没有编程语言天生支持Sun RPC

- 使用独立的预编译器rpcgen来使编程语言支持

  • 输入

- 结构定义语言

  • 输出
  • - 服务器主服务例程
  • - 客户端stub函数
  • - 头文件
  • - 数据传唤函数(如果需要)

 

 

 

 

接口定义语言 Interface Definition Language

  • rpcgen用IDL生成stub函数
  • 定义一个PRC程序:RPC过程的集合
  • 结构如下

type definitions

program identifier{

       version version_id{

              procedure list

       }=value;

       ...

}=value;

program PROG{

       version PROG1{

          void PROC_A(int) = 1;

       }=1;

}=0x3a3afeeb;

数据类型

  • 常量 constants
  • - 将一个#define转换为整型值
  • - const MAXSIZE = 10
  • 结构体 structures
  • - 和C语言中的结构体类似,rpcgen转换结构体定义,然后为结构的名称加一个typedef

struct intpair {int a, b};

struct intpair {int a; int b;};

typedef struct intpair intpair;

 

 

 

 

数据类型

  • 枚举 enumerations
  • - 和C语言相似

       enum state {BUSY=1, IDLE=2, TRANSIT=3};

  • 联合体 unions
  • - 和C语言差别较大
  • - 结构体是一个基于不同标准数据类型的特殊声明
  • - 举例如下

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创建例程

  • 创建一个由RPC定义名字的程序

- 小写

- 由自定义名字,版本号和"svc"三部分组成,例如BLIP, blip_1_svc

  • 程序的参数是一个指向在IDL指明的数据类型的指针
  • 默认情况是每个函数只有一个参数

- 可以使用使用结构体传递多个值

- 在较新的版本中rpcgen对这个限制放宽了,但仍是作为默认情况

  • 程序必须返回一个指向IDL指明的数据类型的指针
  • 由于server stub需要使用返回结果,所以结果必须是静态变量

 

 

 

 

 

一个简单的RPC程序

  • 由一个拥有两个函数的stand-alone程序开始

- 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型作为输入,然后返回字符串

  • IDL

program DATE_PROG {

       version DATE_VERS {

              long BIN_DATE(void) = 1;

              string STR_DATE(long) = 2

       } = 1;

} = 0x31423456;

  • IDL的后缀名".x",编译命令 rpcgen -C date.x

 

 

产生服务器函数:rpcgen的模板

  • 我们可以使用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;

}

 

 

产生服务器函数:插入代码

  • 现在从之前stand-alone代码中复制

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"

  • 使用clnt_create初始化RPC连接

CLIENT *c1;

c1 = clnt_create(server, DATE_PROG, DATE_VERS, "netpath");

  • 程序名和版本号已经在date.h中定义过
  • "netpath"通过读取NETPATH环境变量来决定使用TCP还是UDP
  • 通过联络服务器的RPC名称服务器(端口映射)来找到发送请求的端口

 

 

产生客户端函数:修改远程函数的调用

  • 客户端对bin_date和str_date的调用发生了变化

- 给函数添加版本号

- 添加一个客户端的句柄作为参数(由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);

}

  • 如果bin_date_1调用成功,时间将被正确打印

 

 

产生客户端函数:检查RPC错误(2

  • 对于调用str_date是一样的

char **sresult;

if((sresult = str_date_1(lresult, c1)) == NULL)

{

       clnt_perror(cl, server);

       exit(1);

}

  • 如果str_date_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

  • - 注意RPC_SVC_FG使服务器作为一个前台程序运行而不是后台程序
  • 运行服务器

$ ./server

  • 运行客户端

$ ./client -h remus

time on localhost is 970457832

date is Sun Oct 1 23:37:12 2000

 

 

 

 

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值