RPC编程指导

本文详细介绍远程过程调用(RPC)的概念与实现步骤,包括RPC的工作原理、接口定义语言(IDL)的使用方法,以及如何利用rpcgen工具生成客户端和服务端代码。并通过一个具体的例子演示了从创建IDL文件到生成并运行客户端和服务端的完整流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

远程过程调用 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

 

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/beff2047/archive/2009/03/27/4028568.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值