1. 程式人生 > >Window RPC 詳細運用

Window RPC 詳細運用

前言

         Thrift是一款由Fackbook開發的可伸縮、跨語言的服務開發框架,該框架已經開源並且加入的Apache專案。Thrift主要功能是:通過自定義的Interface Definition Language(IDL),可以建立基於RPC的客戶端和服務端的服務程式碼,本來是準備研究下Thrift跨語言RPC框架,為了預預熱想先總結下Window RPC的開發經驗,下一期釋出我學習Thrift框架的開發經驗。

          Window 系統下要做RPC通訊,需要先編idl檔案,先製作一個基礎型別檔案ms-dtyp.idl,內容如下

cpp_quote("#ifndef _DTYP_IDL")
cpp_quote("#define _DTYP_IDL")

/* Common data types */
cpp_quote("#ifndef _WINDEF_H")
typedef int BOOL, *PBOOL, *LPBOOL;
typedef unsigned char BYTE, *PBYTE, *LPBYTE;
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
typedef unsigned int DWORD32;
typedef unsigned __int64 DWORD64;
cpp_quote("#endif")
//typedef unsigned long error_status_t;
cpp_quote("#ifndef _WINDEF_H")
typedef int INT, *LPINT;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
typedef signed char INT8;
typedef signed short INT16;
typedef signed int INT32;
typedef signed __int64 INT64;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
typedef signed int LONG32;
typedef signed __int64 LONG64;
cpp_quote("#endif")
typedef unsigned __int64 QWORD;
cpp_quote("#ifndef _WINNT_")
typedef short SHORT;
cpp_quote("#endif")
typedef __int64 TIME;
cpp_quote("#ifndef _WINNT_")
typedef char CHAR, *PCHAR;
typedef unsigned char UCHAR, *PUCHAR;
cpp_quote("#endif")
cpp_quote("#ifndef _WINDEF_H")
typedef unsigned int UINT;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
typedef unsigned __int64 UINT64;
cpp_quote("#endif")
cpp_quote("#ifndef _WINNT_")
typedef unsigned long ULONG, *PULONG;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
typedef unsigned int ULONG32;
typedef unsigned __int64 ULONG64;
cpp_quote("#endif")
cpp_quote("#ifndef _WINNT_")
typedef unsigned __int64 ULONGLONG;
typedef unsigned short USHORT;
cpp_quote("#endif")
cpp_quote("#ifndef _WINDEF_H")
typedef unsigned short WORD, *PWORD, *LPWORD;
cpp_quote("#endif")
cpp_quote("#ifndef _WINNT_")
typedef long LONG, *PLONG;
cpp_quote("#endif")
cpp_quote("#ifndef _WINDEF_H")
typedef long *LPLONG;
cpp_quote("#endif")
cpp_quote("#ifndef _WINNT_")
typedef signed __int64 LONGLONG;
cpp_quote("#endif")

cpp_quote("#ifndef _WINDEF_H")
typedef float FLOAT;
cpp_quote("#endif")
cpp_quote("#ifndef __wtypes_h__")
typedef double DOUBLE;
cpp_quote("#endif")

cpp_quote("#ifndef _WINNT_")
typedef BYTE BOOLEAN, *PBOOLEAN;
cpp_quote("#endif")
cpp_quote("#ifndef _BASETSD_H_")
#ifdef _WIN64
typedef __int64 LONG_PTR;
typedef unsigned __int64 ULONG_PTR;
#else
typedef LONG LONG_PTR;
typedef ULONG ULONG_PTR;
#endif
typedef ULONG_PTR SIZE_T;
typedef ULONG_PTR DWORD_PTR;
cpp_quote("#endif")
typedef DWORD NET_API_STATUS;
cpp_quote("#ifndef _WINNT_")
typedef ULONGLONG DWORDLONG, *PDWORDLONG;
cpp_quote("#endif")
typedef DWORD HCALL;
//typedef DWORD HRESULT;

cpp_quote("#ifndef _WINNT_")
typedef void *HANDLE;
typedef void /*VOID,*/ *PVOID;
cpp_quote("#endif")
cpp_quote("#ifndef __WINE_RPCDCE_H")
typedef void *RPC_BINDING_HANDLE;
cpp_quote("#endif")
typedef [context_handle] void *PCONTEXT_HANDLE;
typedef PCONTEXT_HANDLE *PPCONTEXT_HANDLE;

cpp_quote("#ifndef _WINNT_")
typedef wchar_t WCHAR, *PWCHAR;
cpp_quote("#if 0")
typedef wchar_t UNICODE;
cpp_quote("#endif")
typedef const char *LPCSTR;
typedef const wchar_t *LPCWSTR;
typedef char *PSTR, *LPSTR;
typedef wchar_t *LPWSTR, *PWSTR;
cpp_quote("#endif")
typedef const wchar_t *LMCSTR;
typedef WCHAR *LMSTR;
cpp_quote("#ifndef __wtypes_h__")
typedef WCHAR *BSTR;
cpp_quote("#endif")

cpp_quote("#if 0")
#ifdef Unicode
typedef LPCWSTR LPCTSTR;
typedef LPWSTR LPTSTR;
typedef WCHAR TCHAR;
#else
typedef LPCSTR LPCTSTR;
typedef LPSTR LPTSTR;
typedef CHAR TCHAR;
#endif
cpp_quote("#endif")

/* Common data structures */

cpp_quote("#if 0")

typedef struct _FILETIME {
    DWORD dwLowDateTime;
    DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

typedef struct _GUID {
    DWORD Data1;
    WORD Data2;
    WORD Data3;
    BYTE Data4[8];
} GUID, UUID, *PGUID;

typedef struct _LARGE_INTEGER {
    LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;

typedef DWORD LCID;

cpp_quote("#endif")

typedef struct _RPC_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    [size_is(MaximumLength/2), length_is(Length/2)] LPWSTR Buffer;
} RPC_UNICODE_STRING, *PRPC_UNICODE_STRING;

cpp_quote("#if 0")

typedef struct _SYSTEMTIME {
    WORD wYear;
    WORD wMonth;
    WORD wDayOfWeek;
    WORD wDay;
    WORD wHour;
    WORD wMinute;
    WORD wSecond;
    WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;

typedef struct _UINT128 {
    UINT64 lower;
    UINT64 upper;
} UINT128, *PUINT128;

typedef struct _ULARGE_INTEGER {
    ULONGLONG QuadPart;
} ULARGE_INTEGER, *PULARGE_INTEGER;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    [size_is(MaximumLength/2), length_is(Length/2)] LPWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

cpp_quote("#endif")

/* Constructed security types */

cpp_quote("#if 0")

typedef struct _SID_IDENTIFIER_AUTHORITY {
    BYTE Value[6];
} SID_IDENTIFIER_AUTHORITY;

typedef struct _SID {
    BYTE Revision;
    BYTE SubAuthorityCount;
    SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
    [size_is(SubAuthorityCount)] DWORD SubAuthority[*];
} SID, *PSID;

typedef struct _ACCESS_MASK {
    DWORD ACCESS_MASK;
} ACCESS_MASK, *PACCESS_MASK;

typedef struct _ACE_HEADER {
    UCHAR AceType;
    UCHAR AceFlags;
    USHORT AceSize;
} ACE_HEADER, *PACE_HEADER;

typedef struct _ACCESS_ALLOWED_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} ACCESS_ALLOWED_ACE, *PACCESS_ALLOWED_ACE;

typedef struct _ACCESS_ALLOWED_OBJECT_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD Flags;
    GUID ObjectType;
    GUID InheritedObjectType;
    DWORD SidStart;
} ACCESS_ALLOWED_OBJECT_ACE, *PACCESS_ALLOWED_OBJECT_ACE;

typedef struct _ACCESS_DENIED_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} ACCESS_DENIED_ACE, *PACCESS_DENIED_ACE;

typedef struct _ACCESS_ALLOWED_CALLBACK_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} ACCESS_ALLOWED_CALLBACK_ACE, *PACCESS_ALLOWED_CALLBACK_ACE;

typedef struct _ACCESS_DENIED_CALLBACK_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} ACCESS_DENIED_CALLBACK_ACE, *PACCESS_DENIED_CALLBACK_ACE;

typedef struct _ACCESS_ALLOWED_CALLBACK_OBJECT_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD Flags;
    GUID ObjectType;
    GUID InheritedObjectType;
    DWORD SidStart;
} ACCESS_ALLOWED_CALLBACK_OBJECT_ACE, *PACCESS_ALLOWED_CALLBACK_OBJECT_ACE;

typedef struct _ACCESS_DENIED_CALLBACK_OBJECT_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD Flags;
    GUID ObjectType;
    GUID InheritedObjectType;
    DWORD SidStart;
} ACCESS_DENIED_CALLBACK_OBJECT_ACE, *PACCESS_DENIED_CALLBACK_OBJECT_ACE;

typedef struct _SYSTEM_AUDIT_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} SYSTEM_AUDIT_ACE, *PSYSTEM_AUDIT_ACE;

typedef struct _SYSTEM_AUDIT_CALLBACK_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} SYSTEM_AUDIT_CALLBACK_ACE, *PSYSTEM_AUDIT_CALLBACK_ACE;

typedef struct _SYSTEM_MANDATORY_LABEL_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD SidStart;
} SYSTEM_MANDATORY_LABEL_ACE, *PSYSTEM_MANDATORY_LABEL_ACE;

typedef struct _SYSTEM_AUDIT_CALLBACK_OBJECT_ACE {
    ACE_HEADER Header;
    ACCESS_MASK Mask;
    DWORD Flags;
    GUID ObjectType;
    GUID InheritedObjectType;
    DWORD SidStart;
} SYSTEM_AUDIT_CALLBACK_OBJECT_ACE, *PSYSTEM_AUDIT_CALLBACK_OBJECT_ACE;

typedef struct _ACL {
    UCHAR AclRevision;
    UCHAR Sbz1;
    USHORT AclSize;
    USHORT AceCount;
    USHORT Sbz2;
} ACL, *PACL;

typedef struct _SECURITY_DESCRIPTOR {
    UCHAR Revision;
    UCHAR Sbz1;
    USHORT Control;
    ULONG Owner;
    ULONG Group;
    ULONG Sacl;
    ULONG Dacl;
} SECURITY_DESCRIPTOR, *PSECURITY_DESCRIPTOR;

typedef DWORD SECURITY_INFORMATION, *PSECURITY_INFORMATION;

cpp_quote("#endif")

typedef struct _RPC_SID {
    UCHAR Revision;
    UCHAR SubAuthorityCount;
    SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
    [size_is(SubAuthorityCount)] DWORD SubAuthority[];
} RPC_SID, *PRPC_SID;

cpp_quote("#endif /* _DTYP_IDL */")

然後結合ms-dtyp.idl裡面的型別,製作需要生成通訊介面的IDL檔案,比如我現在就做個簡單的RPC請求資料介面檔案PostTest.idl:

#include "ms-dtyp.idl"

[
  uuid(D0FECC64-60E2-4d88-A0FD-39210BB009CA),
  version(2.0)
]


interface PostTest
{
DWORD RpcGetResult( 
    [unique][string][in] LPWSTR lpParam,
    [out, string, size_is(*lpcchResult + 1)] LPWSTR lpResult,
    [out][in] DWORD *lpcchResult);

BOOL RpcSafeExit();
}

到這裡,我們的RPC的IDL介面檔案製作完成,如需豐富IDL檔案裡的通訊介面,只需多加幾個類似的RpcGetResult函式即可

        下一步,我們開始編譯IDL檔案生成對應的.h,.c檔案,分別用於客戶端服務端專案中,Window RPC的編譯方法有兩種,一種是用MIDL.exe,自己用everything搜素,可以搜素到本地的MIDL.exe路徑,把兩個IDL檔案複製到MIDL.exe同目錄,然後執行MIDL.exe  PostTest.idl命令即可生成PostTest_h.h,PostTest_c.c,PostTest_s.c三個檔案,這三個檔案就能直接用到專案中了,第二種編譯方法也非常簡單,我一般都是用第二種方法,就是新建一個隨便命名的空專案,然後將兩個IDL檔案加入到專案,編譯專案即可在專案目錄生成PostTest_h.h,PostTest_c.c,PostTest_s.c三個檔案

           最後就是將這三個檔案用到專案中了,我直接上程式碼,至於為啥我程式碼這麼用這麼寫,你們可以自己再多查資料,我這邊只簡單介紹下用法

複製PostTest_h.h,PostTest_s.c檔案到伺服器專案目錄中,新增檔案到專案中,編寫服務端呼叫main函式

WinRPCServer.cpp

// WinRPCServer.cpp : 定義控制檯應用程式的入口點。
//#include "stdafx.h"
#include <Windows.h>
#include <string>
#include <rpc.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Rpcrt4.lib")
#include "PostTest_h.h"
using namespace std;



void * __RPC_USER MIDL_user_allocate(size_t len)
{
	return (malloc(len));
}

void __RPC_USER MIDL_user_free(void* ptr)
{
	free(ptr);
}

DWORD RpcGetResult(handle_t IDL_handle,LPWSTR lpParam,LPWSTR lpResult,DWORD *lpcchResult)
{
	wstring strType = lpParam;
	wchar_t *szResult = new wchar_t[100];
	wmemset(szResult,0,100);
	if (strType == L"01")
	{
		wcscpy(szResult,L"1111111\r\n");
	}
	else
	{
		wcscpy(szResult,L"2222222\r\n");
	}

	if (*lpcchResult <= wcslen(szResult))
	{
		wcsncpy_s(lpResult,*lpcchResult,szResult,*lpcchResult - 1);
		lpResult[*lpcchResult - 1] = '\0';
		*lpcchResult--;
	}
	else
	{
		wcscpy_s(lpResult,*lpcchResult,szResult);
		*lpcchResult = wcslen(lpResult);
	}
	return 1;
}


BOOL RpcSafeExit(handle_t IDL_handle)
{
	//ExitProcess( 0 );
	return TRUE;
}
BOOL StartRPC() 
{
	/*	wchar_t *pArgv = (wchar_t*)param;*/
	RPC_STATUS status = 0;
	unsigned int nMinCalls = 1;
	unsigned int nMaxCalls = 20;
	// 	int iNum = 0;
	// 	iNum =  _wtoi(pArgv);
	char szAppName[256] = {0};
	sprintf_s(szAppName,256,"WANG_XIAOMING_RPC_GLOBAL_VER_001");

	status = RpcServerUseProtseqEpA((unsigned char *)"ncalrpc", RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
		(unsigned char *)szAppName, NULL );
	if ( status != 0 )
	{
		return FALSE;
	}
	OutputDebugStringA("服務端啟動成功");

	status = RpcServerRegisterIfEx(
		PostTest_v2_0_s_ifspec,
		NULL,
		NULL,
		RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH,
		0,
		NULL);
	if ( status != 0 )
	{
		return FALSE;
	}

	status = RpcServerListen(
		nMinCalls,
		nMaxCalls,
		TRUE );
	return TRUE;
}


int main(int argc, char* argv[])
{
	if (StartRPC())
	{
		while (1)
		{
			Sleep(1000);
		}
	}
	printf("Close");
	getchar();
	return 0;
}

複製PostTest_h.h,PostTest_c.c檔案到客戶端請求專案目錄中,新增檔案到專案中,編寫客戶端呼叫main函式

WinRPCClient.cpp

// WinRPCClient.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include <Windows.h>
#include <rpc.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Rpcrt4.lib")
#include "PostTest_h.h"

RPC_BINDING_HANDLE		g_RpcLink;		//RPC連線


void * __RPC_USER MIDL_user_allocate(size_t len)
{
	return (malloc(len));
}

void __RPC_USER MIDL_user_free(void* ptr)
{
	free(ptr);
}



bool InitRpc()
{
	bool bRet = true;
	RPC_STATUS status;
	unsigned char * pszStringBinding = NULL;
	int iSpc = 1;
	char szSpc[50] = {0};
	sprintf_s(szSpc,"WANG_XIAOMING_RPC_GLOBAL_VER_001");
	status = RpcStringBindingComposeA(
		NULL,
		(unsigned char *)"ncalrpc", //本地的RPC
		NULL,
		(unsigned char *)szSpc,
		NULL,
		&pszStringBinding );
	if ( status != 0 )
	{
		return false;
	}
	status = RpcBindingFromStringBindingA(pszStringBinding, &g_RpcLink );
	if ( status != 0 )
	{
		return false;
	}
	printf("Server RpcInit Suc\r\n");
	return true;
}


bool CheckRpcConnect()
{
	if (g_RpcLink == NULL)
	{
		if (!InitRpc())
		{
			return false;
		}
	}

	RpcTryExcept
	{
		RPC_STATUS status;
		status = RpcMgmtIsServerListening( g_RpcLink );
		if (status == RPC_S_OK)
		{
			printf("ServerListening Suc\r\n");
			return true;
		}
	}
	RpcExcept(1)
	{
		return false;
	}
	RpcEndExcept
		return false;
}


//初始化RPC連線鏈
bool SendRpcRequest(wchar_t *pParam,wchar_t *pResult,DWORD &iLen)
{
	RpcTryExcept
	{
		if (RpcGetResult(g_RpcLink,pParam,pResult,&iLen) != 0 )
		{
			return true;
		}
		return true;;
	}
	RpcExcept(1)
	{
		ULONG ulCode = RpcExceptionCode();
		return false;
	}
	RpcEndExcept
	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{

	if (CheckRpcConnect())
	{
		wchar_t* pResult = new wchar_t[1024];
		wmemset(pResult,0,1024);
		DWORD dwLen = 1024;
		
		SendRpcRequest((LPWSTR)L"01",pResult,dwLen);
		wprintf(pResult);
	}
	
	printf("Close");
	getchar();
	return 0;
}

然後先執行server程式,後執行client,就可以檢視到請求的資料了,我這邊的例子用的是本地RPC呼叫,如需運用網路RPC呼叫,可自行查詢相關例子,修改對應引數