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
随机推荐
- Python with语句的概率,不多说了直接上代码!
python中的with语句用于访问资源.它确保执行指定的__exit__(“清理”)操作,而不管释放被访问资源的处理过程中的错误或异常,例如读取和写入文件后自动关闭.线程中锁的自动获取和释放等. p ...
- php 即点即改
html代码 <td><span id="list_order">{$vo.list_order}</span> </td> < ...
- Java——方法的重写(覆盖)
2.2方法的重写(覆盖)(override,orverwrite) 2.2.1 什么时候方法要进行重写? 如果父类中的方法已经无法满足当前子类的业务需求,需要将父类中的方法进行重新写一遍.就是要改变父 ...
- bzoj1034题解
[解题思路] 广义田忌赛马的贪心模型.如果当前实力最差的马比对手实力最差的马强,则匹配:如果当前实力最强的马比对手实力最强的马强,亦匹配:若上述两点均不成立,拿己方最差的马去匹配对手最强的马.复杂度O ...
- 给标签设置disabled属性后提交不了数据
项目中遇到给select标签添加disabled属性然后提交表单的时候不能提交该表单的数据到后台, readonly属性对提交数据没有限制,但是readonly属性对radio.select.chec ...
- kafk的数据消费快速的原因
kafka为什么消费数据很快呢? 1.数据的顺序读写 2.页缓存(操作系统层面) https://blog.csdn.net/gdj0001/article/details/80136364
- hdu多校第十场 1003 (hdu6693) Valentine's Day 贪心/概率
题意: 有许多物品,每个物品有一定概率让女朋友开心.你想让女朋友开心且只开心一次,让你挑一些物品,使得这个只开心一次的概率最大,求最大概率. 题解: 设物品i让女朋友开心的概率为$p_i$ 若你挑选了 ...
- maven项目打成jar包之前需要在pom文件做的准备工作
文章目录 pom.xml 很多时候在eclipse中没问题java -jar没有主类,或者classnotfound pom.xml <build> <plugins> < ...
- 剑指offer——18打印从1到最大的n位数
题目: 输入数字n,按顺序打印出从1到最大的n位十进制数.比如输入3,则打印出1.2.3一直到最大的3位数999. 题解: 注意大数溢出问题,故使用字符串更靠谱 class Solution { pu ...
- jquery的attr获取表单checked 布尔值问题
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...