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
随机推荐
- 从零开始搭建系统1.4——MySql安装及配置
安装环境:CentOS7 64位 ,安装MySQL5.7 1.创建mysql目录 2.在MySQL官网中下载YUM源rpm安装包:http://dev.mysql.com/downloads/repo ...
- 笔记40 Spring Web Flow——订单流程(构建订单)
二.订单子流程 在识别完顾客之后,主流程的下一件事情就是确定他们想要什么类型 的披萨.订单子流程就是用于提示用户创建披萨并将其放入订单中 的,如下图所示. showOrder状态位于订单子流程的中心位 ...
- BIO、NIO和AIO
BIO(Blocking I/O)同步阻塞I/O 这是最基本与简单的I/O操作方式,其根本特性是做完一件事再去做另一件事,一件事一定要等前一件事做完,这很符合程序员传统的顺序来开发思想,因此BIO模型 ...
- python_django_The requested URL /sunck/login/sunck/showmain/ was not found on this server.错误
在我url匹配过程中出现了这样一个错误: 网页显示: Not Found The requested URL /sunck/login/sunck/showmain/ was not found on ...
- 47. List中特有的方法
集合的体系:--------------| Collection 单列集合的根接口----------| List 如果实现了List接口的集合类,该类具备的特点是:有序,可重复---------- ...
- Linkedlist 详解
基本介绍 Linkedlist基于链表的动态数组(双向链表): 可以被当作堆栈(后进先出).队列(先进先出)或双端队列进行操作. 数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低, ...
- Ruby 语法
Ruby 语法 让我们编写一个简单的 Ruby 程序.所有的 Ruby 文件扩展名都是 .rb.所以,把下面的源代码放在 test.rb 文件中. 实例 #!/usr/bin/ruby -w puts ...
- 高危预警|RDP漏洞或引发大规模蠕虫爆发,用户可用阿里云免费检测服务自检,建议尽快修复
2019年9月6日,阿里云应急响应中心监测到Metasploit-framework官方在GitHub空间公开了针对Windows远程桌面服务远程命令执行漏洞(CVE-2019-0708)的利用代码. ...
- 59 cuda 不同版本__half冲突问题 —— "__half" has no member "x"
0 引言 深度学习中常常用到half类型的半精度浮点数,但是cpu本身是不支持half的,因此需要进行转换. 1 half - float转换 参考了某博主的github,链接如下. https:// ...
- [转]C++ 使用Makefile文件
//*********list class.h**********class tdate {private:int month;int day;int year;public:tdate();tdat ...