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远程过程调用实例详解的更多相关文章

  1. RPC框架调用过程详解

    RPC框架调用过程详解 2017年09月16日 21:14:08 荷叶清泉 阅读数 6275   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...

  2. linux基础-磁盘阵列(RAID)实例详解

    磁盘阵列(RAID)实例详解 raid技术分类 软raid技术 硬raid技术 Raid和lvm的区别 为什么选择用raid RAID详解 RAID-0 RAID-1 RAID-5 Raid-10 R ...

  3. Cocos2d-x 3.X手游开发实例详解

    Cocos2d-x 3.X手游开发实例详解(最新最简Cocos2d-x手机游戏开发学习方法,以热门游戏2048.卡牌为例,完整再现手游的开发过程,实例丰富,代码完备,Cocos2d-x作者之一林顺和泰 ...

  4. JavaScript学习笔记-实例详解-类(二)

    实例详解-类(二)   //===给Object.prototype添加只读\不可枚举\不可配置的属性objectId(function(){ Object.defineProperty(Object ...

  5. JavaScript学习笔记-实例详解-类(一)

    实例详解-类(一): //每个javascript函数(除了bind())都自动拥有一个prototype对象// 在未添加属性或重写prototype对象之前,它只包含唯一一个不可枚举属性const ...

  6. Entity Framework实例详解

    Entity Framework Code First的默认行为是使用一系列约定将POCO类映射到表.然而,有时候,不能也不想遵循这些约定,那就需要重写它们.重写默认约定有两种方式:Data Anno ...

  7. 免费的HTML5连载来了《HTML5网页开发实例详解》连载(二)

    最近新浪.百度.腾讯.京东.大众点评.淘宝等流行的网站都加大了招聘HTML5的力度,HTML5开发人员成了抢手货,本次连载的是由大众点评前端工程师和一淘网前端工程师基情奉献的<HTML5网页开发 ...

  8. Linux下rz命令使用的实例详解

    Linux中rz命令和sz命令都可用于文件传输,而rz命令主要用于文件的上传,下面将通过几个实例来给大家详细介绍下Linux下rz命令的用法,一起来学习下吧. rz命令可以批量上传文件,当然也可上传单 ...

  9. 实例详解 DB2 排序监控和调优

    实例详解 DB2 排序监控和调优http://automationqa.com/forum.php?mod=viewthread&tid=2882&fromuid=2

随机推荐

  1. eclipse打包jar及第三方jar包一起导出(生成SDK)

    一.前言: 因公司需求,需要将某个工具类供外部使用,所以需要生成jar文件.但是jar内还包含了第三方的jar,普通的打包方式无法将lib下的第三方jar包提取. 这将会导致工具jar无法运行,或Ex ...

  2. 解决mac下,javac命令出现的乱码问题

    今天突然检查我的jdk安装,发现出现了乱码 一,出现乱码

  3. 在GitHub上编辑README.md排版样式

    如何在github的readme.md编辑出好看的样式排版文章呢...? 参考:https://blog.csdn.net/u012067966/article/details/50736647

  4. 使用osgearth2.9 rex引擎在Qt中黑屏的问题修复

    将osgUtil::RenderStage.cpp中的下列红色代码注释重新编译即可: if ( !colorAttached ) { setDrawBuffer( GL_NONE, true ); s ...

  5. ES6面试 知识点汇总(全)

    近期在复习ES6,针对ES6新的知识点,以问答形式整理一个全面知识和问题汇总.(全干货,适合对ES6有一定理解的同学复习,以及ES6面试.) 一.问:ES6是什么? 答: ES6是新一代的JS语言标准 ...

  6. FFT最新卡常研究

    指针优化并没有什么卵用,反而增大了代码的不可读性. 除了本来的循环顺序优化寻址,在预处理单位复数根时,可以连续存储,以增快寻址速度,细节见代码. 代码给出的是FFT,NTT是一样的. #include ...

  7. Openssl命令的使用

    用途: pkcs8格式的私钥转换工具.它处理在PKCS#8格式中的私钥文件.它可以用多样的PKCS#5 (v1.5 and v2.0)和 PKCS#12算法来处理没有解密的PKCS#8 Private ...

  8. Delphi中绘制圆角矩形的窗体

    制作圆角矩形的窗体: 01.procedure TPortForm.FormCreate(Sender: Tobject); 02.var hr :thandle; 03.begin 04.hr:=c ...

  9. java带jar编译与运行

    javac -classpath ./wxpay-sdk-0.0.3.jar HttpsTest2.java java -cp .:./wxpay-sdk-0.0.3.jar HttpsTest2

  10. HDU5669

    目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:传送门  Portal  原题目描述在最下面.  给你n个点 ...