咱們從一個簡單的 RPC 「Hello, world!」的例子開始。
參考資料:MSDN: Win32 and COM Development -> Networking -> Network Protocols -> Remote Procedure Calls (RPC)
第1步:編寫 IDL(Interface Description Language,接口描述語言)文件
-------------------------------------------------------------------------
IDL 是一個通用的工業標準語言,你們應該不陌生,由於 COM 裏面也是用它來描述接口的。
Hello.idl:
[
uuid("4556509F-618A-46CF-AB3D-ED736ED66477"), // 惟一的UUID,用 GUIDGen 生成
version(1.0)
]
interface HelloWorld
{
// 咱們定義的方法
void Hello([in,string]const char * psz);
void Shutdown(void);
}
一個可選的文件是應用程序配置文件(.acf),它的做用是對 RPC 接口進行配置,例以下面的 Hello.acf 文件:
Hello.acf:
[
implicit_handle(handle_t HelloWorld_Binding)
]
interface HelloWorld
{
}
上面定義了 implicit_handle,這樣客戶端將綁定句柄 HelloWorld_Binding 了,後面的客戶端代碼中咱們會看到。
編譯 IDL 文件:
>midl Hello.idl
Microsoft (R) 32b/64b MIDL Compiler Version 6.00.0366
Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.
Processing .\Hello.idl
Hello.idl
Processing .\Hello.acf
Hello.acf
咱們能夠看到自動生成了 Hello.h, Hello_s.c, Hello_c.c 文件,這些叫作 rpc stub 程序,不過咱們能夠無論這個概念,
咱們只須要知道 Hello.h 裏面定義了一個
extern RPC_IF_HANDLE HelloWorld_v1_0_s_ifspec;
這個 RPC_IF_HANDLE 將在後面用到。
第2步:編寫服務端程序
-------------------------------------------------------------------------
第1步中咱們已經約定了調用的接口,那麼如今咱們開始實現其服務端。代碼以下:
server.c
#include <stdlib.h>
#include <stdio.h>
#include "Hello.h" // 引用MIDL 生成的頭文件
/**
* 這是咱們在IDL 中定義的接口方法
* 須要注意一點,IDL 裏面的聲明是:void Hello([in,string]const char * psz);
* 可是這裏變成了const unsigned char *,爲何呢?
* 參見MSDN 中的MIDL Command-Line Reference -> /char Switch
* 默認的編譯選項,對 IDL 中的char 按照unsigned char 處理
*/
void Hello(const unsigned char * psz)
{
printf("%s\n", psz);
}
/** 這也是咱們在IDL 中定義的接口方法,提供關閉server 的機制*/
void Shutdown(void)
{
// 下面的操做將致使 RpcServerListen() 退出
RpcMgmtStopServerListening(NULL);
RpcServerUnregisterIf(NULL, NULL, FALSE);
}
int main(int argc,char * argv[])
{
// 用Named Pipe 做爲RPC 的通道,這樣EndPoint 參數就是Named Pipe 的名字
// 按照Named Pipe 的命名規範,\pipe\pipename,其中pipename 能夠是除了\
// 以外的任意字符,那麼這裏用一個GUID 串來命名,能夠保證不會重複
RpcServerUseProtseqEp((unsigned char *)"ncacn_np", 20, (unsigned char *)"\\pipe\\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL);
// 註冊接口,HelloWorld_v1_0_s_ifspec 是在MIDL 生成的Hello.h 中定義的
RpcServerRegisterIf(HelloWorld_v1_0_s_ifspec, NULL, NULL);
// 開始監聽,本函數將一直阻塞
RpcServerListen(1,20,FALSE);
return 0;
}
// 下面的函數是爲了知足連接須要而寫的,沒有的話會出現連接錯誤
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR *ptr)
{
free(ptr);
}
編譯:
>cl /D_WIN32_WINNT=0x500 server.c Hello_s.c rpcrt4.lib
用於 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 14.00.50727.42 版
版權全部(C) Microsoft Corporation。保留全部權利。
server.c
Hello_s.c
正在生成代碼...
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.
/out:server.exe
server.obj
Hello_s.obj
rpcrt4.lib
編譯時爲何要指定 _WIN32_WINNT=0x500 呢?由於若是沒有的話會報告下面的錯誤:
Hello_s.c(88) : fatal error C1189: #error : You need a Windows 2000 or later to
run this stub because it uses these features:
第3步:編寫客戶端程序
-------------------------------------------------------------------------
客戶端的代碼:
client.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Hello.h" // 引用MIDL 生成的頭文件
int main(int argc, char * argv[])
{
unsigned char * pszStringBinding = NULL;
if ( argc != 2 )
{
printf("Usage:%s <Hello Text>\n", argv[0]);
return 1;
}
// 用Named Pipe 做爲RPC 的通道。參見server.c 中的RpcServerUseProtseqEp() 部分
// 第3 個參數NetworkAddr 若是取NULL,那麼就是鏈接本機服務
// 不然要取\\\\servername 這樣的格式,例如你的計算機名爲jack,那麼就是\\jack
RpcStringBindingCompose( NULL, (unsigned char*)"ncacn_np", /*(unsigned char*)"\\\\servername"*/ NULL, (unsigned char*)"\\pipe\\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL, &pszStringBinding );
// 綁定接口,這裏要和 Hello.acf 的配置一致,那麼就是HelloWorld_Binding
RpcBindingFromStringBinding(pszStringBinding, & HelloWorld_Binding );
// 下面是調用服務端的函數了
RpcTryExcept
{
if ( _stricmp(argv[1], "SHUTDOWN") == 0 )
{
Shutdown();
}
else
{
Hello((unsigned char*)argv[1]);
}
}
RpcExcept(1)
{
printf( "RPC Exception %d\n", RpcExceptionCode() );
}
RpcEndExcept
// 釋放資源
RpcStringFree(&pszStringBinding);
RpcBindingFree(&HelloWorld_Binding);
return 0;
}
// 下面的函數是爲了知足連接須要而寫的,沒有的話會出現連接錯誤
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
{
return(malloc(len));
}
void __RPC_USER midl_user_free(void __RPC_FAR *ptr)
{
free(ptr);
}
編譯:
>cl /D_WIN32_WINNT=0x500 client.c Hello_c.c rpcrt4.lib
用於 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 14.00.50727.42 版
版權全部(C) Microsoft Corporation。保留全部權利。
client.c
Hello_c.c
正在生成代碼...
Microsoft (R) Incremental Linker Version 8.00.50727.42
Copyright (C) Microsoft Corporation. All rights reserved.
/out:client.exe
client.obj
Hello_c.obj
rpcrt4.lib
第4步:測試:
-------------------------------------------------------------------------
運行 server.exe,將彈出一個 console 窗口,等待客戶端調用。
運行客戶端 client.exe:
>client hello
能夠看到 server.exe 的 console 窗口出現 hello 的字符串。
>client shutdown
server.exe 退出。
示例下載
ide