前言
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调用,可自行查询相关例子,修改对应参数
相关工程下载: https://download.youkuaiyun.com/download/u010340160/10815937