RPC远程过程调用实例详解
1、创建IDL文件,定义接口。
IDL文件可以由uuidgen.exe创建。
首先找到系统中uuidgen.exe的位置,如:C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools。在此目录下运行命令"uuidgen
/i /ohello.idl",即可在该位置生成一个IDL文件:hello.idl。文件内容如下:
//hello.idl
[
uuid(b2617491-ba5a-48a9-b388-9f0cee8ec882),
version(1.0)
]
interface INTERFACENAME
{
}
然后,增加接口。如下:
//hello.idl
[
uuid(b2617491-ba5a-48a9-b388-9f0cee8ec882),
version(1.0)
]
interface INTERFACENAME
{
void HelloProc([in,string]unsigned char* szhello);
void ShutDown(void);
}
2、创建acf文件。
hello.acf文件内容如下:
//hello.acf
[
implicit_handle (handle_t hello_IfHandle)
]
interface INTERFACENAME
{
}
注意: 1)hello.idl文件与hello.acf文件中的接口名称(INTERFACENAME)应一致,否则接下来编译的时候会报错。
2)hello.idl文件与hello.acf文件应放在同一目录下。
3、编译IDL文件。
有资料说可以用"midl hello.idl"命令直接进行编译,但是我试过之后,总是提示MIDL1005 的错误,没办法,用vs2008进行编译的,步骤如下
。
首先,创建一个空的项目,如RpcTest将编辑好的hello.idl文件添加至RpcTest项目中。
然后,直接进行编译。
这时就可以看到RpcTest项目的生成目录下有了hello_h.h, hello_c.c, hello_s.c三个文件。其中,hello_h.h文件是客户端和服务器端程序共
同要用到的,hello_c.c是客户端程序需要的,hello_s.c是服务器程序所需要的。
在hello_h.h文件中可以看到hello.idl中所定义的接口实体,一个全局句柄变量(handle_t)以及客户端与服务端的接口句柄名
INTERFACENAME_v1_0_c_ifspec和INTERFACENAME_v1_0_s_ifspec。客户端、服务端应用程序在实时调用将使用接口句柄名。
:
/* interface INTERFACENAME */
/* [implicit_handle][version][uuid] */
void HelloProc(/* [string][in] */ unsigned char *szhello);
void ShutDown( void);
extern handle_t hello_IfHandle;
extern RPC_IF_HANDLE INTERFACENAME_v1_0_c_ifspec;
extern RPC_IF_HANDLE INTERFACENAME_v1_0_s_ifspec;
4、编写服务器程序。
服务端通过调用RPC实现函数RpcServerUseProtseqEp 与RpcServrRegisterIf捆绑信息并提供给客户端,例子程序传递接口句柄名给
RpcServerRegisterIf,其它的参数被置为空,客户端然后调用RpcServerListen函数等待客户端的请求。
服务端应用程序必须包含两个内存管理函数midl_user_allocate与midl_user_free。当远端过程调用向服务端传递参数时,调用这两个函数分
配及释放内存。
除此之外,服务端还应实现具体的接口函数功能。详细代码如下。
//server.cpp
#include <iostream>
using namespace std;
#include "hello_h.h"
int main(void)
{
RPC_STATUS status = 0;
unsigned int mincall = 1;
unsigned int maxcall = 20;
status = RpcServerUseProtseqEp(
(unsigned char *)"ncacn_np",
maxcall,
(unsigned char *)"\\pipe\\hello",
NULL);
if(status != 0){
cout<<"RpcServerUseProtseqEp returns: "<<status<<endl;
return -1;
}
status = RpcServerRegisterIf(
INTERFACENAME_v1_0_s_ifspec,
NULL,
NULL);
if(status != 0){
cout<<"RpcServerRegisterIf returns: "<<status<<endl;
return -1;
}
cout<<"Rpc Server Begin Listening..."<<endl;
status = RpcServerListen(mincall, maxcall, FALSE);
if(status != 0){
cout<<"RpcServerListen returns: "<<status<<endl;
return -1;
}
cin.get();
return 0;
}
/************************************************************************/
/* MIDL malloc & free */
/************************************************************************/
void * __RPC_USER MIDL_user_allocate(size_t len)
{
return (malloc(len));
}
void __RPC_USER MIDL_user_free(void*ptr)
{
free(ptr);
}
/************************************************************************/
/* Interfaces */
/************************************************************************/
void HelloProc(unsigned char *szhello)
{
cout<<szhello<<endl;
}
void ShutDown(void)
{
RPC_STATUS status = 0;
status = RpcMgmtStopServerListening(NULL);
if(status != 0){
cout<<"RpcMgmtStopServerListening returns: "<<status<<"!"<<endl;
}
status = RpcServerUnregisterIf(NULL, NULL, FALSE);
if(status != 0){
cout<<"RpcServerUnregisterIf returns: "<<status<<"!"<<endl;
}
}
5、编译服务端程序。
再次利用刚才的空项目RpcTest。
1)首先将刚刚加入的hello.idl文件从项目中移除。
2)然后加入hello_h.h, hello_s.c, server.cpp三个文件。
3)为项目加入rpc库文件:rpcrt4.lib。
4)编译生成RpcTest.exe,更名为server.exe。
6、编写客户端程序。
hello_c.c 源文件中定义了hello_h.h,它由MIDL生成,在它内部又预定义了rpc.h与rncndr.h它们包含了客户端、服务端应用程序所使用的实时
程序及数据类型,客户端管理着它到服务端的连接,客户端应用程序调用实时函数建立用来连接服务端的句柄,当远端过程调用完成时再释放
它。RpcStringBindingCompose 把代表句柄和为字符串绑定而配置内存的成份组装成字符串。RpcBindingFromStringBinding 根据上一个字符
串为客户端应用程序创建一个服务端绑定句柄。接口端点的指定,方法很多,最终方式取决于使用的协议,例子中使用的是Named pipes,它使
用的IDL字符串是“ncacn_np”,则终点名称就填写”\\pipes\\idlfilename”。
RPC异常处理通过一整套宏处理可以使你控制外部应用程序代码出错引起的异常现象,如有发生,将会调用RpcExcept模块,在这里你需要清除
内存并安全退出。远端过程调用结束后,客户端首先调用RpcStringFree函数,释放设置字符串捆绑的内存,然后调用RpcBindgFree()去释放句
柄。
详细代码如下。
//client.cpp
#include <iostream>
#include <string>
using namespace std;
#include "hello_h.h"
void doRpcCall();
int main(int argc, char** argv)
{
int i = 0;
RPC_STATUS status = 0;
unsigned char * pszNetworkAddr = NULL;
unsigned char * pszStringBinding = NULL;
for(i = 1; i < argc; i++){
if(strcmp(argv[i], "-ip") == 0){
pszNetworkAddr = (unsigned char*)argv[++i];
break;
}
}
status = RpcStringBindingCompose(NULL,
(unsigned char *) "ncacn_np",
pszNetworkAddr,
(unsigned char *)"\\pipe\\hello",
NULL,
&pszStringBinding);
if(status != 0){
cout<<"RpcStringBindingCompose returns: "<<status<<"!"<<endl;
return -1;
}
cout<<"pszStringBinding = "<<pszStringBinding<<endl;
status = RpcBindingFromStringBinding(pszStringBinding, &hello_IfHandle);
if(status != 0){
cout<<"RpcBindingFromStringBinding returns: "<<status<<"!"<<endl;
return -1;
}
doRpcCall();
status = RpcStringFree(&pszStringBinding);
if(status != 0)
cout<<"RpcStringFree returns: "<<status<<"!"<<endl;
status = RpcBindingFree(&hello_IfHandle);
if(status != 0)
cout<<"RpcBindingFree returns: "<<status<<"!"<<endl;
cin.get();
return 0;
}
void doRpcCall(void)
{
char buff[1024];
RpcTryExcept{
while(true){
cout<<"Please input a string param for Rpc call:"<<endl;
cin.getline(buff, 1023);
if(strcmp(buff, "exit") == 0 || strcmp(buff, "quit") == 0){
ShutDown();
}
else{
HelloProc((unsigned char*)buff);
cout<<"call helloproc succeed!"<<endl;
}
}
}
RpcExcept(1){
unsigned long ulCode = RpcExceptionCode();
cout<<"RPC exception occured! code: "<<ulCode<<endl;
}
RpcEndExcept
}
void * __RPC_USER MIDL_user_allocate(size_t len)
{
return (malloc(len));
}
void __RPC_USER MIDL_user_free(void* ptr)
{
free(ptr);
}
7、编译客户端程序。
再次利用刚才的空项目RpcTest。
1)首先将刚刚加入的hello_h.h等文件从项目中全部移除。
2)然后加入hello_h.h, hello_c.c, client.cpp三个文件。
3)为项目加入rpc库文件:rpcrt4.lib。
4)编译生成RpcTest.exe,更名为client.exe。
8、大功告成。
OK,到现在,已经有了客户端、服务端应用程序的可执行文件。
1)首先运行server.exe。
2)而后,在client.exe所在的目录下用命令行"client.exe -ip 192.168.1.146"来启动客户端程序并与服务器端相连。
3)在client的窗口内输入任意字符串,回车后可看到server窗口上有显示。
4)在client窗口内输入exit或quit,server窗口关闭。
RPC远程过程调用实例详解的更多相关文章
- RPC框架调用过程详解
RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...
- linux基础-磁盘阵列(RAID)实例详解
磁盘阵列(RAID)实例详解 raid技术分类 软raid技术 硬raid技术 Raid和lvm的区别 为什么选择用raid RAID详解 RAID-0 RAID-1 RAID-5 Raid-10 R ...
- Cocos2d-x 3.X手游开发实例详解
Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...
- JavaScript学习笔记-实例详解-类(二)
实例详解-类(二) //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...
- JavaScript学习笔记-实例详解-类(一)
实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...
- Entity Framework实例详解
Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表.然而,有时候,不能也不想遵循这些约定,那就需要重写它们.重写默认约定有两种方式:Data Anno ...
- 免费的HTML5连载来了《HTML5网页开发实例详解》连载(二)
最近新浪.百度.腾讯.京东.大众点评.淘宝等流行的网站都加大了招聘HTML5的力度,HTML5开发人员成了抢手货,本次连载的是由大众点评前端工程师和一淘网前端工程师基情奉献的<HTML5网页开发 ...
- Linux下rz命令使用的实例详解
Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,下面将通过几个实例来给大家详细介绍下Linux下rz命令的用法,一起来学习下吧. rz命令可以批量上传文件,当然也可上传单 ...
- 实例详解 DB2 排序监控和调优
实例详解 DB2 排序监控和调优http://automationqa.com/forum.php?mod=viewthread&tid=2882&fromuid=2
随机推荐
- ubuntu系统设置密码报错 Module is unknown
修改账户密码报错 # passwd 报错信息 passwd: Module is unknown passwd: password unchanged 修改配置文件 # cd /etc/pam.d ...
- 启动eclipse出现JVM terminated. Exit code=127 错误解决办法
https://blog.csdn.net/wpzsidis/article/details/72954387 进去第二次又错
- vue组件基础之创建与使用
一.创建组件 <script src="vue.js"></script> <!--引入vue.js文件--> <div id=" ...
- context:component-scan报错
文件头补上 xmlns:context=”http://www.springframework.org/schema/context” xsi:schemaLocation=”http://www.s ...
- 强大的httpClientUtils
<!-- https://mvnrepository.com/artifact/com.arronlong/httpclientutil --> <dependency> &l ...
- js简单图片切换
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title> ...
- Struts功能详解——ActionForm
ActionForm ActionForm用于封装用户的请求参数,而请求参数是通过JSP页面的表单域传递过来的.因此应保证ActionForm的参数,与表单域的名字相同. ActionForm ...
- HBase的应用场景及特点
一.Hbase能做什么?1. 海量数据存储:上百亿行 x 上百万列并没有列的限制当表非常大的时候才能发挥这个作用, 最多百万行的话,没有必要放入hbase中2. 准实时查询:百亿行 x 百万列,在百毫 ...
- NOIp2018集训test-9-1(am)
1.最大值 可以用FWT水过去,李巨写了FWT结果中途爆int了炸了几十分好像. 我乱搞了一下把除了大数据有or的搞出来然后90,还是蛮划算的.我yy的做法: 1.xor 字典树上贪心, 一开始我打了 ...
- 让nginx支持patchinfo,(支持codeigniter,thinkphp,ZF等框架)
nginx 的config配置: server { listen ; server_name xxx; ....if (!-e $request_filename) { rewrite ^/(.*)$ ...