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. ORACLE check view

    select owner as schema_name,        view_name from sys.all_views where VIEW_NAME like 'P%' order by ...

  2. DEV插件下的控件Grid和Gridlookupedit控件的结合使用

    创建GridlookupEtid控件 设置其对应属性: 设置属性 this.gridLookUpEdit1.Properties.TextEditStyle = DevExpress.XtraEdit ...

  3. Sublime Text3中MarkDown的使用

    前言 当我们想要在Sublime文本编辑器中编辑markdown时,需要先安装markdown插件,因为Sublime里默认没有安装该插件,同时在编辑markdown文本时可以实时预览编辑效果. 具体 ...

  4. jq-demo-购物车

    首页 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title& ...

  5. 【优化】碎片OPTIMIZE

    来看看手册中关于 OPTIMIZE 的描述: OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... 如果您已经删除 ...

  6. linux环境下创建domain

    首先进入weblogic的安装目录,具体如下: cd /wls/Oracle/Middleware/Oracle_Home/wlserver/common/bin 图形化创建 1.下载xmanager ...

  7. ios 查看UIView的层次继承关系工具

    http://stackoverflow.com/questions/5150186/how-do-i-inspect-the-view-hierarchy-in-ios https://github ...

  8. 如何通过编程发现Java死锁

    本文由 ImportNew - rookie_sam 翻译自 Dzone.欢迎加入翻译小组.转载请见文末要求. 死锁是指,两个或多个动作一直在等待其他动作完成而使得所有动作都始终处在阻塞的状态.想要在 ...

  9. 拾遗:『rhel6系列的开、关机脚本』

    一.系统脚本位置及含义 /etc/inittab./etc/init/* 存放开关机配置文件 /etc/init.d/* 服务脚本,是一个到/etc/rc.d/init.d/的软链接 /etc/rc. ...

  10. 剑指offer——26树的子结构

    题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构)   题解: 注意,所谓的子结构,是树的形状和值相同,并非判断B是不是A的一部分[如果是这样,那就是 ...