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
随机推荐
- 标准 I/O (带缓冲)
标准IO中分文本流和二进制流 windows系统中:(1) 二进制流 - 换行符 '\n' (2) 文本流 - 换行符 ‘\r' '\n' linux 系统中:不区分文本流和二进制流:换行符 - ...
- 多台服务器-SSH免密登录设置
在4台服务器-SSH免密登录设置,如以下4台服务器 master1 node001 node002 node003 我想在master1对4台服务器进行拉取或者分发任务或者是集群服务器的批量操作,但是 ...
- java script两个列表之间移动数据
<select name="b1" id="hao" style="width:100px; height:200px;" size= ...
- thinkphp 快速缓存
如果你的存储数据没有有效期的需求,那么系统还提供了一个快速缓存方法F可以用来更快的操作. 大理石平台厂家 F方法可以支持不同的存储类型,如果是文件类型的话,默认保存在DATA_PATH目录下面. 快速 ...
- VScode中写vue代码 Ctrl+/添加注释失效
1.点击列表的文件——>首选项——>键盘快捷方式,在里面查看 Ctrl+/ 是否有冲突 2.查看右下角的选择语言模式是否是Vue,如下图
- iOS开发之3D Touch
1.简介 3DTouch是在6s之后苹果的一项技术,只能在6s及其以上机型真机运行,Xcode的模拟器是不支持的. Quick Actions(点击icon的快捷方式) Peek&Pop(应用 ...
- due to a StackOverflowError. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies. The class hierarchy being processed was [org.jaxen.util.AncestorAxisIt
七月 31, 2019 4:39:01 下午 org.apache.catalina.startup.VersionLoggerListener log信息: Server version: Apac ...
- class11_messagebox 弹窗
最终的运行效果图(程序见序号2) #!/usr/bin/env python# -*- coding:utf-8 -*-# -------------------------------------- ...
- 分布式存储glusterfs
什么是glusterfs? Glusterfs是一个开源分布式文件系统,具有强大的横向扩展能力,可支持数PB存储容量的数干客户端,通过网络互联成一个并行的网络文件系统.具有可扩展性.高性能.高可用性等 ...
- webpack3
6月20号webpack推出了3.0版本,官方也发布了公告.根据公告介绍,webpack团队将未来版本的改动聚焦在社区提出的功能需求,同时将保持一个快速.稳定的发布节奏.本文主要依据公告内容,简单介绍 ...