关于rpc简介、原理、实例-缘于difx

news/2025/3/31 10:34:10/

关于rpc简介、原理、实例-缘于difx

       今天稍微研究了一下关于rpc的东东,主要是因为Pro.Z碰到的问题,在解决了以后,对rpc产生了一些兴趣,考虑到我目前的程序也可以从比较底层的socket方式概率为rpc方式,看的过程中,看到了一个网上的实例教程,估计已经转载了N遍了,所以有几个错误,包括rpc unknown protocalconfliction definecannot register service等,在测试的时候也进行了一些改正,并且进行了测试。

       单单是这个小程序,就可以做一个时间服务器性质的程序了。

简介

在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益发展的今天已无法适应实际需求。总所周知,传统过程调用模式无法充分利用网络上其他主机的资源(如CPUMemory等),也无法提高代码在实体间的共享程度,使得主机资源大量浪费。

      通过RPC我们可以充分利用非共享内存的多处理器环境(例如通过局域网连接得多台工作站),这样可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。

RPC的结构原理及其调用机制

如前所述RPC其实也是种C/S的编程模式,有点类似C/S Socket 编程模式,但要比它

更高一层。当我们在建立RPC服务以后,客户端的调用参数通过底层的RPC传输通道,可以是UDP,也可以是TCP(也即TI-RPC—无关性传输),并根据传输前所提供的目的地址及RPC上层应用程序号转至相应的RPC应用程序服务端,且此时的客户端处于等待状态,直至收到应答或Time Out超时信号。具体的流程图如F1。当服务器端获得请求消息,则会根据注册RPC时告诉RPC系统的例程入口地址,执行相应的操作,并将结果返回至客户端。

 
 clip_image002

F1

当一次RPC调用结束后,相应线程发送相应的信号,客户端程序才会继续运行。

在这个过程中,一个远程过程是有三个要素来唯一确定的:程序号、版本号和过程号。

程序号是用来区别一组相关的并且具有唯一过程好的远程过程。一个程序可以有一个或几个不同的版本,而每个版本的程序都包含一系列能被远程调用的过程,通过版本的引入,使得不同版本下的RPC能同时提供服务。每个版本都包含有许多可供远程调用的过程,每个过程则有其唯一标示的过程号。

基于RPC的应用系统开发

通过以上对RPC原理的简介后,我们再来继续讨论如何来开发基于RPC的应用系统。

一般而言在开发RPC时,我们通常分为三个步骤:

a、 定义说明客户/服务器的通信协议:这里所说的通信协议是指定义服务过程的名称、调用参数的数据类型和返回参数的数据类型,还包括底层传输类型(可以是UDPTCP),当然也可以由RPC底层函数自动选择连接类型建立TI-RPC。最简单的协议生成的方法是采用协议编译工具,常用的有Rpcgen,我会在后面实例中详细描述其使用方法。

b、  开发客户端程序。

c、  开发服务器端程序。

开发客户端和服务器端的程序时,RPC提供了我们不同层次的开发例程调用接口。不同层次的接口提供了对RPC不同程度控制。一般可分为5个等级的编程接口,接下来我们分别讨论一下各层所提供的功能函数。

简单层例程

简单层是面向普通RPC应用,为了快速开发RPC应用服务而设计的,他提供了如下功能函数。

  函数名

             功能描述

Rpc_reg( )

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

Rpc_call( )

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

Rpc_Broadcast( )

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

                                

高层例程

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

          

  函数名

             功能描述

Clnt_create( )

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

Clnt_create_timed( )

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

Svc_create( )

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

Clnt_call()

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

中间层例程

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

  函数名

             功能描述

Clnt_tp_create( )

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

Clnt_tp_create_timed( )

定义最大传输时延

Svc_tp_creaet( )

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

Clnt_call( )

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

                

专家层例程

这层提供了更多的一系列与传输相关的功能调用,它提供了如下功能函数。

 

  函数名

             功能描述

Clnt_tli_create( )

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

Svc_tli_create( )

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

Rpcb_set( )

通过调用rpcbindRPC服务和网络地址做映射

Rpcb_unset( )

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

Rpcb_getaddr( )

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

Svc_reg( )

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

Svc_ureg( )

删除有svc_reg( ) 所建的关联

Clnt_call( )

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

底层例程

该层提供了所有对传输选项进行控制的调用接口,它提供了如下功能函数。

  函数名

             功能描述

Clnt_dg_create( )

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

Svc_dg_create( )

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

Clnt_vc_create( )

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

Svc_vc_create( )

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

Clnt_call( )

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

实例介绍

以下我将通过实例向读者介绍通过简单层RPC的实现方法。通常在此过程中我们将使用RPC协议编译工具—RpcgenRpcgen 工具用来生成远程程序接口模块,它将以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 <stdio.h>

#include <rpc/rpc.h> /* always needed */

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

#include <time.h>

/* Remote version of "printime" */

char ** printime_1_svc(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 <stdio.h>

#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");

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





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.htime_svc.ctime_clnt.c

   再用系统提供的gcc进行C的编译,命令如下:

   $gcc rtime.c time_clnt.c -o rtime –lnsl                            //客户端编译

$gcc time_proc.c time_svc.c -o time_server  –lnsl     error//服务器端编译

编译成功后即可在Server端运行time_server,立即将该服务绑定在rpc服务端口上提供服务。在客户端运行./rdate hostname msg (msg 是一字符串,笔者用来测试时建立的),立即会返回hostname 端的时间。

出错问题

Cannot register service

RPC: Authentication error; why = Client credential too weak unable to register (TIMEPROG, PRINTIMEVERS, udp)

解决方法:使用root账户即可

time_proc.c:7: error: conflicting types for printime_1?

源程序time_proc.c中报错

解决方法,改为char ** printime_1_svc(char **msg,struct svc_req *req)即可。

RPC: Unknown protocol

      这个原因可能是rpc依赖包没有安装,或者使用函数clnt_create的时候,最后一个参数应该不使用visible,而是tcp。


http://www.ppmy.cn/news/445229.html

相关文章

InstallScript是否已经支持DIFx?

在InstallShield 2010&#xff0c;支持DIFx 2.1.1。现在Basic MSI&#xff0c;InstallScript和InstallScript MSI三种工程类型都支持DIFx。 更多InstallShield 有关技术问题&#xff0c;欢迎访问应用程序打包技术论坛-AppInstall&#xff1a;www.appinstall.cn InstallShield …

difx rpc unavailable

difx rpc unavailable 问题及解决方法 我记得去年貌似就碰到这个问题&#xff0c;不过只是不同的发行版而已&#xff0c;今天是Fedora 17&#xff0c;Pro.Z的机器。 问题就是difx的calcserver程序是需要调用rpc的&#xff0c;而大部分情况下&#xff0c;出错的原因是因为rpc支…

rpc简介、原理、实例-缘于difx

简介 RPC(Remote Procedure Call&#xff0c;远程过程调用)是建立在Socket之上的,出于一种类比的愿望,在一台机器上运行的主程序,可以调用另一台机器上准备好的子程序,就像LPC(本地过程调用).越底层&#xff0c;代码越复杂、灵活性越高、效率越高&#xff1b;越上层&#xff0c…

告诉你C盘里的每个文件夹都是干什么用的!

1.Documents and Settings是什么文件&#xff1f;答案&#xff1a;是系统用户设置文件夹&#xff0c;包括各个用户的文档、收藏夹、上网浏览信息、配置文件等。补&#xff1a;这里面的东西不要随便删除&#xff0c;这保存着所有用户的文档和账户设置&#xff0c;如果删除就会重…

ma5671怎么设置_电信/联通/移动,更换华为MA5671光猫详细教程。

电信/联通/移动,更换华为MA5671光猫详细教程。 2020-11-30 23:48:00 1142点赞 8200收藏 367评论 你是AMD Yes党?还是intel和NVIDIA的忠实簇拥呢?最新一届#装机大师赛#开始啦!本次装机阵营赛分为3A红组、intel NVIDIA蓝绿组、混搭组还有ITX组,实体or虚拟装机都能参与,可使…

VMWare16环境搭建Ubuntu20.10

VMWare16环境搭建Ubuntu20.10 一、工具准备二、虚拟机软件安装三、VMWare安装系统的准备过程四、安装Ubuntu20.04LTS系统 一、工具准备 1、VMWare16.11&#xff08;2021.4.1发布&#xff09; 官方资源下载&#xff1a;https://www.vmware.com/cn/products/workstation-pro/wor…

【Linux】常用命令——绝世无双

目录 一、文件基本操作命令 1. ls 展示目录列表 2. pwd 显示当前所在路径 3. cd 切换目录 4. mkdir、touch 目录和文件的创建 5. rmdir、rm 目录和文件的删除 6. cp 复制文件或目录 7. mv 移动和重命名文件或目录 8. vi 、vim 编辑文件 9. cat 、tac 显示文件内容 10…

利用python写一个自动获取局域网ip地址并开启服务器绕过校园网设备限制的小工具

前言 ​ 这个学年学校把宽带运营商从之前的联通大哥哥换成了如今的电信小弟弟&#xff0c;使用体验可谓一落千丈&#xff0c;最主要的原因是电信不再像联通提供拨号上网的账号和密码&#xff0c;改成了强制使用天翼校园的程序来进行认证上网&#xff0c;而且限制只能一台设备在…