RPC远程过程调用实例详解

来源:互联网 发布:怎么关闭淘宝音乐 编辑:程序博客网 时间:2024/04/29 11:21

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窗口关闭。

 

参考:

http://blog.csdn.net/hizxc/archive/2009/05/16/4193781.aspx

http://hi.baidu.com/ishb/blog/item/f8aa8a8078a5c1d39023d912.html

原创粉丝点击