使用者自定義結構資料與VARIANT轉換
cheungmine
将使用者自定義的C結構資料存儲成VARIANT類型,需要時再将VARIANT類型轉為使用者自定義的結構資料,有十分現實的意義,既然我們不想為這樣的結構資料寫一個COM包裝類。雖然有很多方法和手段生成這樣的VARIANT,但是,多數時候可能需要一個更加簡單的,靈活的方法。我在做遠端過程調用的C接口時,忽然聯想到,既然RPC可以把任何資料以位元組的形式發送,那麼,就可以利用這個機制,把結構打包成位元組數組。而位元組資料是可以很友善地存儲在VARIANT中。
這個過程是廣為人知的,但是,真正把結構列內建位元組數組,如果不想使用某些标稱的序列化的方法,而全部自己寫,的确要費一番功夫。不是
技術有多難,是很繁瑣。我把前2年寫的代碼翻出來,簡單調用一下,就有了這篇文章。采用我的方法,C/C++程式員可以把自己定義的結構放到VARIANT、CComVariant、COleVariant等各種VARIANT中,也可以反向轉換。而VARIANT是可以很友善地在COM接口中傳遞。這樣,就多了一種在自動化COM接口中傳遞自定義結構的手段。
不多說廢話,全部内容見下面的代碼,我還會上傳整個工程。
struct2variant.cpp 如下:
///////////////////////////////////////////////////////////////////////
// struct2variant.cpp
// 2010-6
// 下面的程式示範了如何在使用者自定義的結構和VARIANT類型之間轉換
// 保留所有權利
//
#include "stdafx.h"
#include "rpc/rpcapi.h"
#include <assert.h>
#ifdef _DEBUG
# pragma comment(lib, "rpc/rpclib/debug/rpclib.lib")
#else
# pragma comment(lib, "rpc/rpclib/release/rpclib.lib")
#endif
// 自定義結構辨別
#define MY_STRUCT_ID 101 // 辨別結構的任意數字
typedef struct _PointF
{
double x;
double y;
}PointF;
// 自定義結構
typedef struct _MyStruct
CHAR id[32];
CHAR server[130];
CHAR instance[10];
CHAR userid[32];
BOOL isdraw;
ULONG token;
LONG timeout;
LONG keepalive;
LONG reserved;
BOOL status;
LONG capacity;
LONG volatile counter;
// 說明如何儲存變長數組
SHORT numPts;
PointF *ptArray;
}MyStruct;
void PrintfMyStruct(const char *desc, MyStruct *data)
printf("==========%s==========/n", desc);
printf("id=%s/n", data->id);
printf("server=%s/n", data->server);
printf("instance=%s/n", data->instance);
printf("userid=%s/n", data->userid);
printf("isdraw=%d/n", data->isdraw);
printf("token=%d/n", data->token);
printf("timeout=%d/n", data->timeout);
printf("keepalive=%d/n", data->keepalive);
printf("reserved=%d/n", data->reserved);
printf("status=%d/n", data->status);
printf("capacity=%d/n", data->capacity);
printf("counter=%d/n", data->counter);
printf("numPts=%d/n", data->numPts);
for(int i=0; i<data->numPts; i++)
printf("ptArray[%d]= (x=%.3lf, y=%.3lf)/n", i, data->ptArray[i].x, data->ptArray[i].y);
}
static HRESULT CreateStreamFromBytes(BYTE *inBytes, DWORD cbSize, IStream **ppStm)
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, 0);
ATLASSERT(hGlobal);
if (!hGlobal)
return E_OUTOFMEMORY;
CComPtr<IStream> spStm;
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &spStm);
ATLASSERT(hr == S_OK);
if (hr != S_OK || spStm == 0){
GlobalFree(hGlobal);
return hr;
}
ULONG ulWritten = 0;
hr = spStm->Write(inBytes, cbSize, &ulWritten);
if (hr != S_OK || ulWritten != cbSize)
return E_FAIL;
return spStm.CopyTo(ppStm);
////////////////////////////////////////////////////////////////////
// 列集自定義資料到VARIANT
static void MarshallMyStruct(MyStruct *inData, VARIANT *outVar)
assert(inData && outVar);
rpc_invoke_descriptor inv = {0};
rpc_invoke_init(&inv, MY_STRUCT_ID, 14); // 14個參數
// 下面每個結構成員參數都需要按次序綁定
rpc_invoke_bind_param(&inv,
0, /* 結構成員參數索引0-based */
RPCT_STRING, /* 指明字元串數組類型 */
(void*)inData->id, /* 指向實際資料的指針 */
strlen(inData->id)+1, /* 隻需要儲存有效的資料 */
0 /*0 表示我們不具有id的所有權(不負責釋放記憶體) */
);
1,
RPCT_STRING,
(void*)inData->server,
strlen(inData->server)+1,
2,
(void*)inData->instance,
strlen(inData->instance)+1,
3,
(void*)inData->userid,
strlen(inData->userid)+1,
4,
RPCT_BOOL,
(void*) &inData->isdraw,
0, /* 不是數組, 為0 */
5,
RPCT_ULONG,
(void*) &inData->token,
0,
6,
RPCT_LONG,
(void*) &inData->timeout,
7,
(void*) &inData->keepalive,
8,
(void*) &inData->reserved,
9,
(void*) &inData->status,
10,
(void*) &inData->capacity,
11,
(void*) &inData->counter,
12,
RPCT_SHORT,
(void*) &inData->numPts,
13,
RPCT_DOUBLE, /* 簡單結構的成員類型 */
(void*) inData->ptArray, /* 記憶體結構=[x1,y1,x2,y2,...,xn,yn], 千萬不可寫成:(void*) &inData->ptArray */
inData->numPts * (sizeof(PointF)/sizeof(double)), /* double類型成員的數目=點數x2 */
/* 計算調用消息的位元組總數 */
dword_t cbOut = rpc_invoke_get_size(&inv);
char *outBuf = (char*) malloc(cbOut);
/* 列集全部資料到totalBuf中 */
rpc_invoke_marshal(&inv, outBuf, cbOut);
//////////////////////////////////////////////////////////
// 到此,我們已經在outBuf中儲存了結構的全部資料,
// 下面把資料轉換成IStream進而轉換成VARIANT
CreateStreamFromBytes((BYTE*)outBuf, cbOut, &spStm);
VariantInit(outVar);
outVar->vt = VT_UNKNOWN;
outVar->punkVal = (IUnknown*) spStm.Detach();
free(outBuf);
rpc_invoke_clear_all(&inv);
// 散集VARIANT到自定義資料
static void UnmarshallMyStruct(VARIANT *inVar, MyStruct *outData)
assert(inVar && outData);
assert(inVar->vt==VT_UNKNOWN && inVar->punkVal);
HGLOBAL hGlobal = 0;
HRESULT hr = GetHGlobalFromStream((IStream*)inVar->punkVal, &hGlobal);
assert(hr==S_OK);
size_t cbData = GlobalSize(hGlobal);
char *pbData = (char*) GlobalLock(hGlobal);
char *rpcHdr = pbData;
assert(cbData >= RPC_INVOKE_HEADER_SIZE);
assert(rpcHdr[0]=='R' && rpcHdr[1]=='C');
rpc_invoke_descriptor inv={0};
rpc_invoke_init(&inv, 0, 0);
inv.encPkg = rpcHdr[2];
inv.encHdr = rpcHdr[3];
assert (inv.encHdr == 0);
inv.ordinal = word_ntoh(*((word_t*)(rpcHdr+20))); // 方法序号: MY_STRUCT_ID
assert(inv.ordinal == MY_STRUCT_ID);
inv.invToken = dword_ntoh(*((dword_t*)(rpcHdr+4))); // 使用者辨別
inv.totalBytes = dword_ntoh(*((dword_t*)(rpcHdr+8))); // 總消息位元組數(也許是壓縮的)
inv.bitsFlag = word_ntoh(*((word_t*)(rpcHdr+12))); // 标志
inv.bigEndian = GET_BIT(inv.bitsFlag, RPC_BIGENDIAN_BIT); // 客戶方列集的位元組次序
inv.reserved = word_ntoh(*((word_t*)(rpcHdr+14))); // 保留值
inv.result = dword_ntoh(*((dword_t*)(rpcHdr+16))); // 傳回值
inv.num_params = word_ntoh(*((word_t*)(rpcHdr+22))); // 參數數目
rpc_invoke_error err={0};
rpc_invoke_unmarshal(&inv, rpcHdr, inv.totalBytes, &inv.params_list, &inv.num_params, &err);
GlobalUnlock(hGlobal);
strncpy_s(outData->id, sizeof(outData->id), (char*) inv.params_list[0].param_bytes, sizeof(outData->id)-1);
strncpy_s(outData->server, sizeof(outData->server), (char*) inv.params_list[1].param_bytes, sizeof(outData->server)-1);
strncpy_s(outData->instance, sizeof(outData->instance), (char*) inv.params_list[2].param_bytes, sizeof(outData->instance)-1);
strncpy_s(outData->userid, sizeof(outData->userid), (char*) inv.params_list[3].param_bytes, sizeof(outData->userid)-1);
outData->isdraw = PARAMVALUE(inv.params_list, 4, BOOL);
outData->token = PARAMVALUE(inv.params_list, 5, ULONG);
outData->timeout = PARAMVALUE(inv.params_list, 6, LONG);
outData->keepalive = PARAMVALUE(inv.params_list, 7, LONG);
outData->reserved = PARAMVALUE(inv.params_list, 8, LONG);
outData->status = PARAMVALUE(inv.params_list, 9, BOOL);
outData->capacity = PARAMVALUE(inv.params_list, 10, LONG);
outData->counter = PARAMVALUE(inv.params_list, 11, LONG);
outData->numPts = PARAMVALUE(inv.params_list, 12, SHORT);
// 為輸出配置設定 ptArray
outData->ptArray = (PointF*) malloc(sizeof(PointF)*outData->numPts);
memcpy(outData->ptArray, inv.params_list[13].param_bytes, sizeof(PointF)*outData->numPts);
int main(int argc, CHAR* argv[])
MyStruct data, data2;
CComVariant var; // 這個var 可以存儲在任何需要使用到的地方
// 初始化結構data
strcpy_s(data.id, sizeof(data.id), "13890");
strcpy_s(data.server, sizeof(data.server), "localhost");
strcpy_s(data.instance, sizeof(data.instance), "port:6755");
strcpy_s(data.userid, sizeof(data.userid), "cheungmine");
data.isdraw = 1;
data.token = 54321;
data.timeout = 3000;
data.keepalive = 6500;
data.reserved=0;
data.status = 0;
data.capacity = 4096;
data.counter = 99;
data.numPts = 16;
data.ptArray = (PointF*) malloc(data.numPts*sizeof(PointF));
for(int i=0; i<data.numPts; i++){
data.ptArray[i].x = 100+i;
data.ptArray[i].y = 200+i;
PrintfMyStruct("input MyStruct", &data);
// 使用者的結構轉換為VARIANT: data=>var
MarshallMyStruct(&data, &var);
free(data.ptArray);
// ...使用var
// VARIANT轉為使用者的結構: var=>data
UnmarshallMyStruct(&var, &data2);
PrintfMyStruct("output MyStruct", &data2);
free(data2.ptArray);
///////////////////////////////////////////////////////////////////////
// struct2variant.cpp
// 2010-6
// 下面的程式示範了如何在使用者自定義的結構和VARIANT類型之間轉換
// 保留所有權利
//
#include "stdafx.h"
#include "rpc/rpcapi.h"
#include <assert.h>
#ifdef _DEBUG
# pragma comment(lib, "rpc/rpclib/debug/rpclib.lib")
#else
# pragma comment(lib, "rpc/rpclib/release/rpclib.lib")
#endif
// 自定義結構辨別
#define MY_STRUCT_ID 101 // 辨別結構的任意數字
typedef struct _PointF
{
double x;
double y;
}PointF;
// 自定義結構
typedef struct _MyStruct
CHAR id[32];
CHAR server[130];
CHAR instance[10];
CHAR userid[32];
BOOL isdraw;
ULONG token;
LONG timeout;
LONG keepalive;
LONG reserved;
BOOL status;
LONG capacity;
LONG volatile counter;
// 說明如何儲存變長數組
SHORT numPts;
PointF *ptArray;
}MyStruct;
void PrintfMyStruct(const char *desc, MyStruct *data)
printf("==========%s==========/n", desc);
printf("id=%s/n", data->id);
printf("server=%s/n", data->server);
printf("instance=%s/n", data->instance);
printf("userid=%s/n", data->userid);
printf("isdraw=%d/n", data->isdraw);
printf("token=%d/n", data->token);
printf("timeout=%d/n", data->timeout);
printf("keepalive=%d/n", data->keepalive);
printf("reserved=%d/n", data->reserved);
printf("status=%d/n", data->status);
printf("capacity=%d/n", data->capacity);
printf("counter=%d/n", data->counter);
printf("numPts=%d/n", data->numPts);
for(int i=0; i<data->numPts; i++)
printf("ptArray[%d]= (x=%.3lf, y=%.3lf)/n", i, data->ptArray[i].x, data->ptArray[i].y);
}
static HRESULT CreateStreamFromBytes(BYTE *inBytes, DWORD cbSize, IStream **ppStm)
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, 0);
ATLASSERT(hGlobal);
if (!hGlobal)
return E_OUTOFMEMORY;
CComPtr<IStream> spStm;
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &spStm);
ATLASSERT(hr == S_OK);
if (hr != S_OK || spStm == 0){
GlobalFree(hGlobal);
return hr;
ULONG ulWritten = 0;
hr = spStm->Write(inBytes, cbSize, &ulWritten);
if (hr != S_OK || ulWritten != cbSize)
return E_FAIL;
return spStm.CopyTo(ppStm);
////////////////////////////////////////////////////////////////////
// 列集自定義資料到VARIANT
static void MarshallMyStruct(MyStruct *inData, VARIANT *outVar)
assert(inData && outVar);
rpc_invoke_descriptor inv = {0};
rpc_invoke_init(&inv, MY_STRUCT_ID, 14); // 14個參數
// 下面每個結構成員參數都需要按次序綁定
rpc_invoke_bind_param(&inv,
0, /* 結構成員參數索引0-based */
RPCT_STRING, /* 指明字元串數組類型 */
(void*)inData->id, /* 指向實際資料的指針 */
strlen(inData->id)+1, /* 隻需要儲存有效的資料 */
0 /*0 表示我們不具有id的所有權(不負責釋放記憶體) */
);
1,
RPCT_STRING,
(void*)inData->server,
strlen(inData->server)+1,
2,
(void*)inData->instance,
strlen(inData->instance)+1,
3,
(void*)inData->userid,
strlen(inData->userid)+1,
4,
RPCT_BOOL,
(void*) &inData->isdraw,
0, /* 不是數組, 為0 */
5,
RPCT_ULONG,
(void*) &inData->token,
0,
6,
RPCT_LONG,
(void*) &inData->timeout,
7,
(void*) &inData->keepalive,
8,
(void*) &inData->reserved,
9,
(void*) &inData->status,
10,
(void*) &inData->capacity,
11,
(void*) &inData->counter,
12,
RPCT_SHORT,
(void*) &inData->numPts,
13,
RPCT_DOUBLE, /* 簡單結構的成員類型 */
(void*) inData->ptArray, /* 記憶體結構=[x1,y1,x2,y2,...,xn,yn], 千萬不可寫成:(void*) &inData->ptArray */
inData->numPts * (sizeof(PointF)/sizeof(double)), /* double類型成員的數目=點數x2 */
/* 計算調用消息的位元組總數 */
dword_t cbOut = rpc_invoke_get_size(&inv);
char *outBuf = (char*) malloc(cbOut);
/* 列集全部資料到totalBuf中 */
rpc_invoke_marshal(&inv, outBuf, cbOut);
//////////////////////////////////////////////////////////
// 到此,我們已經在outBuf中儲存了結構的全部資料,
// 下面把資料轉換成IStream進而轉換成VARIANT
CreateStreamFromBytes((BYTE*)outBuf, cbOut, &spStm);
VariantInit(outVar);
outVar->vt = VT_UNKNOWN;
outVar->punkVal = (IUnknown*) spStm.Detach();
free(outBuf);
rpc_invoke_clear_all(&inv);
// 散集VARIANT到自定義資料
static void UnmarshallMyStruct(VARIANT *inVar, MyStruct *outData)
assert(inVar && outData);
assert(inVar->vt==VT_UNKNOWN && inVar->punkVal);
HGLOBAL hGlobal = 0;
HRESULT hr = GetHGlobalFromStream((IStream*)inVar->punkVal, &hGlobal);
assert(hr==S_OK);
size_t cbData = GlobalSize(hGlobal);
char *pbData = (char*) GlobalLock(hGlobal);
char *rpcHdr = pbData;
assert(cbData >= RPC_INVOKE_HEADER_SIZE);
assert(rpcHdr[0]=='R' && rpcHdr[1]=='C');
rpc_invoke_descriptor inv={0};
rpc_invoke_init(&inv, 0, 0);
inv.encPkg = rpcHdr[2];
inv.encHdr = rpcHdr[3];
assert (inv.encHdr == 0);
inv.ordinal = word_ntoh(*((word_t*)(rpcHdr+20))); // 方法序号: MY_STRUCT_ID
assert(inv.ordinal == MY_STRUCT_ID);
inv.invToken = dword_ntoh(*((dword_t*)(rpcHdr+4))); // 使用者辨別
inv.totalBytes = dword_ntoh(*((dword_t*)(rpcHdr+8))); // 總消息位元組數(也許是壓縮的)
inv.bitsFlag = word_ntoh(*((word_t*)(rpcHdr+12))); // 标志
inv.bigEndian = GET_BIT(inv.bitsFlag, RPC_BIGENDIAN_BIT); // 客戶方列集的位元組次序
inv.reserved = word_ntoh(*((word_t*)(rpcHdr+14))); // 保留值
inv.result = dword_ntoh(*((dword_t*)(rpcHdr+16))); // 傳回值
inv.num_params = word_ntoh(*((word_t*)(rpcHdr+22))); // 參數數目
rpc_invoke_error err={0};
rpc_invoke_unmarshal(&inv, rpcHdr, inv.totalBytes, &inv.params_list, &inv.num_params, &err);
GlobalUnlock(hGlobal);
strncpy_s(outData->id, sizeof(outData->id), (char*) inv.params_list[0].param_bytes, sizeof(outData->id)-1);
strncpy_s(outData->server, sizeof(outData->server), (char*) inv.params_list[1].param_bytes, sizeof(outData->server)-1);
strncpy_s(outData->instance, sizeof(outData->instance), (char*) inv.params_list[2].param_bytes, sizeof(outData->instance)-1);
strncpy_s(outData->userid, sizeof(outData->userid), (char*) inv.params_list[3].param_bytes, sizeof(outData->userid)-1);
outData->isdraw = PARAMVALUE(inv.params_list, 4, BOOL);
outData->token = PARAMVALUE(inv.params_list, 5, ULONG);
outData->timeout = PARAMVALUE(inv.params_list, 6, LONG);
outData->keepalive = PARAMVALUE(inv.params_list, 7, LONG);
outData->reserved = PARAMVALUE(inv.params_list, 8, LONG);
outData->status = PARAMVALUE(inv.params_list, 9, BOOL);
outData->capacity = PARAMVALUE(inv.params_list, 10, LONG);
outData->counter = PARAMVALUE(inv.params_list, 11, LONG);
outData->numPts = PARAMVALUE(inv.params_list, 12, SHORT);
// 為輸出配置設定 ptArray
outData->ptArray = (PointF*) malloc(sizeof(PointF)*outData->numPts);
memcpy(outData->ptArray, inv.params_list[13].param_bytes, sizeof(PointF)*outData->numPts);
int main(int argc, CHAR* argv[])
MyStruct data, data2;
CComVariant var; // 這個var 可以存儲在任何需要使用到的地方
// 初始化結構data
strcpy_s(data.id, sizeof(data.id), "13890");
strcpy_s(data.server, sizeof(data.server), "localhost");
strcpy_s(data.instance, sizeof(data.instance), "port:6755");
strcpy_s(data.userid, sizeof(data.userid), "cheungmine");
data.isdraw = 1;
data.token = 54321;
data.timeout = 3000;
data.keepalive = 6500;
data.reserved=0;
data.status = 0;
data.capacity = 4096;
data.counter = 99;
data.numPts = 16;
data.ptArray = (PointF*) malloc(data.numPts*sizeof(PointF));
for(int i=0; i<data.numPts; i++){
data.ptArray[i].x = 100+i;
data.ptArray[i].y = 200+i;
PrintfMyStruct("input MyStruct", &data);
// 使用者的結構轉換為VARIANT: data=>var
MarshallMyStruct(&data, &var);
free(data.ptArray);
// ...使用var
// VARIANT轉為使用者的結構: var=>data
UnmarshallMyStruct(&var, &data2);
PrintfMyStruct("output MyStruct", &data2);
free(data2.ptArray);
其中:rpcapi.h可以參考如下:
/**
* rpcapi.h [email protected]
* all rights reserved 2009
*/
#ifndef _RPCAPI_H__
#define _RPCAPI_H__
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <process.h> /* _beginthreadex, _endthread */
#include "../rc4/unistd.h"
#include "../rc4/md5.h"
#include "../rc4/rc4.h"
#include "rpctype.h"
#include "rpcerr.h"
#ifdef __cplusplus
extern "C" {
static BOOL _rpc_is_bigendian = (('4321'>>24)=='1');
#define PARAMVALUE(pl,i,type) ((type)(*((type*) pl[i].param_bytes)))
typedef enum
RPC_ENCHEADER_BIT = 0,
RPC_ENCPACKAGE_BIT = 1,
RPC_BIGENDIAN_BIT = 7
}RPC_BITFLAG_ENUM;
* param descriptor
#define RPC_PARAM_HEADER_SIZE 16
typedef struct _rpc_param_descriptor
word_t param_type; // 參數類型:變量類型或結構類型
word_t size_type; // 參數類型尺寸位元組
size_t array_size; // 數組元素數目: 0 不是數組; >0數組
size_t param_size; // 參數資料位元組數=參數類型尺寸位元組*元素數目
void *param_bytes; // 指向參數資料的指針的引用, 不複制資料
BOOL is_owner; // 是否擁有資料. 如果為TRUE, 需要free(param_bytes)
}rpc_param_descriptor;
* invoke descriptor
#define RPC_INVOKE_HEADER_SIZE 24 /* 不可以更改 */
typedef struct _rpc_invoke_descriptor
/* 24位元組頭 + 内容
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|原始位元組數(4)|方法序号(2)|參數數目(2)]
*/
dword_t totalBytes; // 總消息位元組4
word_t bitsFlag; // 标志位2: [7=BigEndian]
word_t reserved; // 保留2
dword_t result; // 傳回值
// 内部值, 調用時,需要列集在bitsFlag中
dword_t invToken;
byte bigEndian;
byte encHdr;
byte encPkg;
// 如果壓縮或加密, 從以下位置内容開始
ushort ordinal; // 方法序号2
ushort num_params; // 參數數目
rpc_param_descriptor *params_list; // 參數清單
// 做為輸入時, 存放臨時值
//
char *pbBuf;
union{
char bVal;
long lVal;
short sVal;
float fVal;
double dVal;
__int64 llVal;
};
}rpc_invoke_descriptor;
* connection descriptor
typedef struct _rpc_connection_descriptor
BOOL is_bigendian; // 位元組順序
dword_t token; // 調用辨別
dword_t timeout; // 逾時
byte enc_hdr; // 頭加密标志
byte enc_pkg; // 參數包加密标志
SOCKET stream; // SOCKET 連接配接
char host[130]; // 主機名或IP位址
char port[6]; // 端口号
rpc_invoke_error last_err; // 最近的錯誤
char buffer[RPC_BUFFSIZE]; // 網絡傳輸緩沖區
}rpc_connection_descriptor;
/*=============================================================================
Private Functions
host byte order and network byte order transform
=============================================================================*/
static int SafeStringSize(const char* psz)
return (int)(psz? (strlen(psz)+1):0);
* More fast than swap_bytes(...) for handle word and dword type
#define swap_word_type(x) ((WORD)(((((WORD)(x))&0x00ff)<<8)|((((WORD)(x))&0xff00)>>8)))
#define swap_dword_type(x) ((DWORD)(((((DWORD)(x))&0xff000000)>>24)|((((DWORD)(x))&0x00ff0000)>>8)|((((DWORD)(x))&0x0000ff00)<<8)|((((DWORD)(x))&0x000000ff)<<24)))
static void swap_bytes(void *wordP, size_t cbSize /*must be 2, 4, 8 */)
size_t i;
byte t;
for(i=0; i<cbSize/2; i++){
t = ((byte *) wordP)[i];
((byte *)wordP)[i] = ((byte *) wordP)[cbSize-i-1];
((byte *) wordP)[cbSize-i-1] = t;
static dword_t dword_hton(dword_t host)
if (!_rpc_is_bigendian)
host = swap_dword_type(host);
return host;
static dword_t dword_ntoh(dword_t net)
net = swap_dword_type(net);
return net;
static word_t word_hton(word_t host)
host = swap_word_type(host);
static word_t word_ntoh(word_t net)
net = swap_word_type(net);
static qword_t qword_hton(qword_t host)
swap_bytes(&host, sizeof(qword_t));
static qword_t qword_ntoh(qword_t net)
swap_bytes(&net, sizeof(qword_t));
static double double_hton(double host)
static double double_ntoh(double net)
static float float_hton(float host)
swap_bytes(&host, sizeof(dword_t));
static float float_ntoh(float net)
swap_bytes(&net, sizeof(dword_t));
socket helper functions
static int setbufsize(SOCKET s, int rcvlen /*RPC_BUFFSIZE*/, int sndlen /*RPC_BUFFSIZE*/)
int rcv, snd;
int rcvl = (int) sizeof(int);
int sndl = rcvl;
if ( getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, &rcvl)==SOCKET_ERROR ||
getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&snd, &sndl)==SOCKET_ERROR )
return SOCKET_ERROR;
if(rcv < rcvlen){
rcv = rcvlen;
rcvl = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, rcvl);
assert(rcvl==0);
if(snd < sndlen){
snd = sndlen;
sndl = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&snd, sndl);
assert(sndl==0);
return 0;
static int sendmsg(SOCKET s, const char* msgbuf, int msglen, int flags)
int ret;
int offset = 0;
int left = msglen;
while (left > 0) {
ret = send(s, &msgbuf[offset], left, flags);
if (ret==SOCKET_ERROR || ret==0)
return ret;
left -= ret;
offset += ret;
assert(offset==msglen);
return offset;
static int recvmsg(SOCKET s, char* msgbuf, int buflen, int flags)
int ret;
int offset = 0;
int left = buflen;
while (left > 0){
ret = recv(s, &msgbuf[offset], left, flags);
return ret;
assert(offset==buflen);
static BOOL sendbulk(SOCKET s, const char* data, int dataSize, int flags, int maxmsg)
int send, ret;
int left = dataSize;
send = left>maxmsg? maxmsg:left;
ret = sendmsg(s, &data[offset], send, flags);
if (ret != send)
return FALSE;
return TRUE;
static BOOL recvbulk(SOCKET s, char* buf, int recvlen, int flags, int maxmsg)
int recv, ret;
int offset = 0;
int left = recvlen;
recv = left>maxmsg? maxmsg:left;
ret = recvmsg(s, &buf[offset], recv, flags);
if (ret != recv)
static LPCSTR errmsg(DWORD dwCode, LPSTR lpszMsgBuf, DWORD dwMsgBufBytes)
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwCode, 0, /* Default language */
lpszMsgBuf,
dwMsgBufBytes,
NULL);
return lpszMsgBuf;
static void rpc_error_clear(rpc_invoke_error *err)
err->err_type = RPC_NO_ERROR;
err->err_code = 0;
*err->err_source = 0;
*err->err_detail = 0;
// [RESM(4)|總消息位元組(4)|err_type(2)|err_code(2)|err_source_size(2)|err_source_string|err_detail_size(2)|err_detail_string]
static BOOL rpc_error_short_msg(SOCKET s, char *buf, int size, rpc_invoke_error *err)
int pos = 0;
word_t w;
ushort err_source_size = (ushort)strlen(err->err_source)+1;
ushort err_detail_size = (ushort)strlen(err->err_detail)+1;
ulong msglen = 4+4+2+2+2+err_source_size+2+err_detail_size;
*buf = 0;
assert((ulong)size>=msglen);
buf[pos++] = 'R';
buf[pos++] = 'E';
buf[pos++] = 'S';
buf[pos++] = 'M';
msglen = dword_hton(msglen);
memcpy(buf+pos, &msglen, 4);
pos += 4;
w = word_hton((word_t)err->err_type);
memcpy(buf+pos, &w, 2);
pos += 2;
w = word_hton((word_t)err->err_code);
w = word_hton((word_t)err_source_size);
pos += 2;
memcpy(buf+pos, err->err_source, err_source_size);
pos += err_source_size;
w = word_hton((word_t)err_detail_size);
memcpy(buf+pos, err->err_detail, err_detail_size);
pos += err_detail_size;
assert((ulong)pos == dword_ntoh(msglen));
if (pos == (ulong)sendmsg(s, buf, pos, 0))
return TRUE;
err->err_type = RPC_SOCKET_ERROR;
err->err_code = WSAGetLastError();
errmsg(err->err_code, err->err_detail, RPC_ERROR_STRING_LEN);
return FALSE;
static void debug_out(const char *debug_file, const char *fmt, ...)
FILE *fp = 0;
va_list ap;
fopen_s(&fp, debug_file, "a+");
assert(fp);
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
fclose(fp);
/*============================================================================
RPC PUBLIC FUNCTIONS
============================================================================*/
* rpc_throw_error
RPCRESULT rpc_throw_error(RPC_ERROR_TYPE err_type, LONG err_code, const char* err_source, rpc_invoke_error *err);
* rpc_invoke_free
* 建立RPC調用描述符
rpc_invoke_descriptor* rpc_invoke_create(rpc_invoke_descriptor **inv, ushort ordinal, ushort num_params);
* rpc_invoke_init
* 初始化RPC調用描述符
rpc_invoke_descriptor *
rpc_invoke_init(rpc_invoke_descriptor *inv, ushort ordinal, ushort num_params);
* rpc_invoke_clear_all
* 清除RPC調用描述符, 此後RPC調用描述符可以重新綁定變量
void rpc_invoke_clear_all(rpc_invoke_descriptor *inv);
* 删除RPC調用描述符, 此後RPC調用描述符将不可用
void rpc_invoke_free(rpc_invoke_descriptor *inv);
* rpc_invoke_set_param
* 設定指定的參數, 傳回參數指針
rpc_param_descriptor* rpc_invoke_set_param(rpc_invoke_descriptor *inv, ushort id, word_t type);
* rpc_param_clear
* 清除參數内容
void rpc_param_clear(rpc_param_descriptor *param);
* rpc_param_free_list
* 清除參數清單内容
void rpc_param_free_list(rpc_param_descriptor *param_list, word_t num_params);
* rpc_connection_create
* 建立RPC連接配接
RPCRESULT rpc_connection_create(rpc_connection_descriptor **conn, const char *host, const char *port, long rcvtimeo, rpc_invoke_error *err);
* rpc_connection_free
* 清除RPC連接配接
void rpc_connection_free(rpc_connection_descriptor *conn);
* rpc_connection_set
* 設定RPC連接配接的屬性
void rpc_connection_set(rpc_connection_descriptor *conn, dword_t token, dword_t timeout, byte encheader, byte encpackage);
* 設定輸入的參數, 傳回參數指針
rpc_param_descriptor* rpc_invoke_bind_param(rpc_invoke_descriptor *inv, ushort id, word_t type, void *vaddr, size_t array_size, BOOL is_owner);
* 計算參數位元組總數
dword_t rpc_param_get_size(rpc_param_descriptor *param);
* 計算調用消息的位元組總數
dword_t rpc_invoke_get_size(rpc_invoke_descriptor *inv);
* rpc_invoke_marshal
* 列集全部資料到totalBuf中
int rpc_invoke_marshal(rpc_invoke_descriptor *inv, char *totalBuf, int totalSize);
* rpc_invokehdr_unmarshal
* 散集調用頭
RPCRESULT rpc_invokehdr_unmarshal(SOCKET sClient, dword_t dwToken, char *rpcHdr, int hdrSize, rpc_invoke_descriptor *inv, rpc_invoke_error *err);
* rpc_invoke_unmarshal
* 散集資料到參數中
RPCRESULT rpc_invoke_unmarshal(rpc_invoke_descriptor *inv,
char *totalBuf,
size_t totalSize,
rpc_param_descriptor **out_params,
word_t *num_params,
rpc_invoke_error *err);
* rpc_param_get_short
* 取得短整數參數值
void rpc_param_get_short (rpc_param_descriptor *param, SHORT *val);
* rpc_param_get_ushort
void rpc_param_get_ushort (rpc_param_descriptor *param, USHORT *val);
* rpc_param_get_long
* 取得整數參數值
void rpc_param_get_long (rpc_param_descriptor *param, LONG *val);
* rpc_param_get_ulong
* 取得無符号整數參數值
void rpc_param_get_ulong (rpc_param_descriptor *param, ULONG *val);
* rpc_param_get_int
void rpc_param_get_int (rpc_param_descriptor *param, INT *val);
* rpc_param_get_double
* 取得雙精度參數值
void rpc_param_get_double (rpc_param_descriptor *param, double *val);
* rpc_invoke_send
* 發送資料
RPCRESULT rpc_invoke_send(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_error *err);
* rpc_invoke_recv
* 接收資料
RPCRESULT rpc_invoke_recv(rpc_connection_descriptor *conn, rpc_invoke_descriptor *outv, rpc_invoke_error *err);
* 執行RPC調用
RPCRESULT rpc_invoke_execute(rpc_connection_descriptor *conn,
rpc_invoke_descriptor *inv,
rpc_invoke_descriptor *outv,
rpc_invoke_error *err);
#endif /* _RPCAPI_H__ */
/**
* rpcapi.h [email protected]
* all rights reserved 2009
*/
#ifndef _RPCAPI_H__
#define _RPCAPI_H__
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <process.h> /* _beginthreadex, _endthread */
#include "../rc4/unistd.h"
#include "../rc4/md5.h"
#include "../rc4/rc4.h"
#include "rpctype.h"
#include "rpcerr.h"
#ifdef __cplusplus
extern "C" {
static BOOL _rpc_is_bigendian = (('4321'>>24)=='1');
#define PARAMVALUE(pl,i,type) ((type)(*((type*) pl[i].param_bytes)))
typedef enum
RPC_ENCHEADER_BIT = 0,
RPC_ENCPACKAGE_BIT = 1,
RPC_BIGENDIAN_BIT = 7
}RPC_BITFLAG_ENUM;
* param descriptor
#define RPC_PARAM_HEADER_SIZE 16
typedef struct _rpc_param_descriptor
word_t param_type; // 參數類型:變量類型或結構類型
word_t size_type; // 參數類型尺寸位元組
size_t array_size; // 數組元素數目: 0 不是數組; >0數組
size_t param_size; // 參數資料位元組數=參數類型尺寸位元組*元素數目
void *param_bytes; // 指向參數資料的指針的引用, 不複制資料
BOOL is_owner; // 是否擁有資料. 如果為TRUE, 需要free(param_bytes)
}rpc_param_descriptor;
* invoke descriptor
#define RPC_INVOKE_HEADER_SIZE 24 /* 不可以更改 */
typedef struct _rpc_invoke_descriptor
/* 24位元組頭 + 内容
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|原始位元組數(4)|方法序号(2)|參數數目(2)]
dword_t totalBytes; // 總消息位元組4
word_t bitsFlag; // 标志位2: [7=BigEndian]
word_t reserved; // 保留2
dword_t result; // 傳回值
// 内部值, 調用時,需要列集在bitsFlag中
dword_t invToken;
byte bigEndian;
byte encHdr;
byte encPkg;
// 如果壓縮或加密, 從以下位置内容開始
ushort ordinal; // 方法序号2
ushort num_params; // 參數數目
rpc_param_descriptor *params_list; // 參數清單
// 做為輸入時, 存放臨時值
char *pbBuf;
union{
char bVal;
long lVal;
short sVal;
float fVal;
double dVal;
__int64 llVal;
};
}rpc_invoke_descriptor;
* connection descriptor
typedef struct _rpc_connection_descriptor
BOOL is_bigendian; // 位元組順序
dword_t token; // 調用辨別
dword_t timeout; // 逾時
byte enc_hdr; // 頭加密标志
byte enc_pkg; // 參數包加密标志
SOCKET stream; // SOCKET 連接配接
char host[130]; // 主機名或IP位址
char port[6]; // 端口号
rpc_invoke_error last_err; // 最近的錯誤
char buffer[RPC_BUFFSIZE]; // 網絡傳輸緩沖區
}rpc_connection_descriptor;
/*=============================================================================
Private Functions
host byte order and network byte order transform
=============================================================================*/
static int SafeStringSize(const char* psz)
return (int)(psz? (strlen(psz)+1):0);
* More fast than swap_bytes(...) for handle word and dword type
#define swap_word_type(x) ((WORD)(((((WORD)(x))&0x00ff)<<8)|((((WORD)(x))&0xff00)>>8)))
#define swap_dword_type(x) ((DWORD)(((((DWORD)(x))&0xff000000)>>24)|((((DWORD)(x))&0x00ff0000)>>8)|((((DWORD)(x))&0x0000ff00)<<8)|((((DWORD)(x))&0x000000ff)<<24)))
static void swap_bytes(void *wordP, size_t cbSize /*must be 2, 4, 8 */)
size_t i;
byte t;
for(i=0; i<cbSize/2; i++){
t = ((byte *) wordP)[i];
((byte *)wordP)[i] = ((byte *) wordP)[cbSize-i-1];
((byte *) wordP)[cbSize-i-1] = t;
static dword_t dword_hton(dword_t host)
if (!_rpc_is_bigendian)
host = swap_dword_type(host);
return host;
static dword_t dword_ntoh(dword_t net)
net = swap_dword_type(net);
return net;
static word_t word_hton(word_t host)
host = swap_word_type(host);
static word_t word_ntoh(word_t net)
net = swap_word_type(net);
static qword_t qword_hton(qword_t host)
swap_bytes(&host, sizeof(qword_t));
static qword_t qword_ntoh(qword_t net)
swap_bytes(&net, sizeof(qword_t));
static double double_hton(double host)
static double double_ntoh(double net)
static float float_hton(float host)
swap_bytes(&host, sizeof(dword_t));
static float float_ntoh(float net)
swap_bytes(&net, sizeof(dword_t));
socket helper functions
static int setbufsize(SOCKET s, int rcvlen /*RPC_BUFFSIZE*/, int sndlen /*RPC_BUFFSIZE*/)
int rcv, snd;
int rcvl = (int) sizeof(int);
int sndl = rcvl;
if ( getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, &rcvl)==SOCKET_ERROR ||
getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&snd, &sndl)==SOCKET_ERROR )
return SOCKET_ERROR;
if(rcv < rcvlen){
rcv = rcvlen;
rcvl = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcv, rcvl);
assert(rcvl==0);
if(snd < sndlen){
snd = sndlen;
sndl = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&snd, sndl);
assert(sndl==0);
return 0;
static int sendmsg(SOCKET s, const char* msgbuf, int msglen, int flags)
int ret;
int offset = 0;
int left = msglen;
while (left > 0) {
ret = send(s, &msgbuf[offset], left, flags);
if (ret==SOCKET_ERROR || ret==0)
return ret;
left -= ret;
offset += ret;
assert(offset==msglen);
return offset;
static int recvmsg(SOCKET s, char* msgbuf, int buflen, int flags)
int ret;
int offset = 0;
int left = buflen;
while (left > 0){
ret = recv(s, &msgbuf[offset], left, flags);
assert(offset==buflen);
static BOOL sendbulk(SOCKET s, const char* data, int dataSize, int flags, int maxmsg)
int send, ret;
int left = dataSize;
send = left>maxmsg? maxmsg:left;
ret = sendmsg(s, &data[offset], send, flags);
if (ret != send)
return FALSE;
return TRUE;
static BOOL recvbulk(SOCKET s, char* buf, int recvlen, int flags, int maxmsg)
int recv, ret;
int offset = 0;
int left = recvlen;
recv = left>maxmsg? maxmsg:left;
ret = recvmsg(s, &buf[offset], recv, flags);
if (ret != recv)
static LPCSTR errmsg(DWORD dwCode, LPSTR lpszMsgBuf, DWORD dwMsgBufBytes)
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwCode, 0, /* Default language */
lpszMsgBuf,
dwMsgBufBytes,
NULL);
return lpszMsgBuf;
static void rpc_error_clear(rpc_invoke_error *err)
err->err_type = RPC_NO_ERROR;
err->err_code = 0;
*err->err_source = 0;
*err->err_detail = 0;
// [RESM(4)|總消息位元組(4)|err_type(2)|err_code(2)|err_source_size(2)|err_source_string|err_detail_size(2)|err_detail_string]
static BOOL rpc_error_short_msg(SOCKET s, char *buf, int size, rpc_invoke_error *err)
int pos = 0;
word_t w;
ushort err_source_size = (ushort)strlen(err->err_source)+1;
ushort err_detail_size = (ushort)strlen(err->err_detail)+1;
ulong msglen = 4+4+2+2+2+err_source_size+2+err_detail_size;
*buf = 0;
assert((ulong)size>=msglen);
buf[pos++] = 'R';
buf[pos++] = 'E';
buf[pos++] = 'S';
buf[pos++] = 'M';
msglen = dword_hton(msglen);
memcpy(buf+pos, &msglen, 4);
pos += 4;
w = word_hton((word_t)err->err_type);
memcpy(buf+pos, &w, 2);
pos += 2;
w = word_hton((word_t)err->err_code);
w = word_hton((word_t)err_source_size);
memcpy(buf+pos, err->err_source, err_source_size);
pos += err_source_size;
w = word_hton((word_t)err_detail_size);
memcpy(buf+pos, err->err_detail, err_detail_size);
pos += err_detail_size;
assert((ulong)pos == dword_ntoh(msglen));
if (pos == (ulong)sendmsg(s, buf, pos, 0))
err->err_type = RPC_SOCKET_ERROR;
err->err_code = WSAGetLastError();
errmsg(err->err_code, err->err_detail, RPC_ERROR_STRING_LEN);
static void debug_out(const char *debug_file, const char *fmt, ...)
FILE *fp = 0;
va_list ap;
fopen_s(&fp, debug_file, "a+");
assert(fp);
va_start(ap, fmt);
vfprintf(fp, fmt, ap);
va_end(ap);
fclose(fp);
/*============================================================================
RPC PUBLIC FUNCTIONS
============================================================================*/
* rpc_throw_error
RPCRESULT rpc_throw_error(RPC_ERROR_TYPE err_type, LONG err_code, const char* err_source, rpc_invoke_error *err);
* rpc_invoke_free
* 建立RPC調用描述符
rpc_invoke_descriptor* rpc_invoke_create(rpc_invoke_descriptor **inv, ushort ordinal, ushort num_params);
* rpc_invoke_init
* 初始化RPC調用描述符
rpc_invoke_descriptor *
rpc_invoke_init(rpc_invoke_descriptor *inv, ushort ordinal, ushort num_params);
* rpc_invoke_clear_all
* 清除RPC調用描述符, 此後RPC調用描述符可以重新綁定變量
void rpc_invoke_clear_all(rpc_invoke_descriptor *inv);
* 删除RPC調用描述符, 此後RPC調用描述符将不可用
void rpc_invoke_free(rpc_invoke_descriptor *inv);
* rpc_invoke_set_param
* 設定指定的參數, 傳回參數指針
rpc_param_descriptor* rpc_invoke_set_param(rpc_invoke_descriptor *inv, ushort id, word_t type);
* rpc_param_clear
* 清除參數内容
void rpc_param_clear(rpc_param_descriptor *param);
* rpc_param_free_list
* 清除參數清單内容
void rpc_param_free_list(rpc_param_descriptor *param_list, word_t num_params);
* rpc_connection_create
* 建立RPC連接配接
RPCRESULT rpc_connection_create(rpc_connection_descriptor **conn, const char *host, const char *port, long rcvtimeo, rpc_invoke_error *err);
* rpc_connection_free
* 清除RPC連接配接
void rpc_connection_free(rpc_connection_descriptor *conn);
* rpc_connection_set
* 設定RPC連接配接的屬性
void rpc_connection_set(rpc_connection_descriptor *conn, dword_t token, dword_t timeout, byte encheader, byte encpackage);
* 設定輸入的參數, 傳回參數指針
rpc_param_descriptor* rpc_invoke_bind_param(rpc_invoke_descriptor *inv, ushort id, word_t type, void *vaddr, size_t array_size, BOOL is_owner);
* 計算參數位元組總數
dword_t rpc_param_get_size(rpc_param_descriptor *param);
* 計算調用消息的位元組總數
dword_t rpc_invoke_get_size(rpc_invoke_descriptor *inv);
* rpc_invoke_marshal
* 列集全部資料到totalBuf中
int rpc_invoke_marshal(rpc_invoke_descriptor *inv, char *totalBuf, int totalSize);
* rpc_invokehdr_unmarshal
* 散集調用頭
RPCRESULT rpc_invokehdr_unmarshal(SOCKET sClient, dword_t dwToken, char *rpcHdr, int hdrSize, rpc_invoke_descriptor *inv, rpc_invoke_error *err);
* rpc_invoke_unmarshal
* 散集資料到參數中
RPCRESULT rpc_invoke_unmarshal(rpc_invoke_descriptor *inv,
char *totalBuf,
size_t totalSize,
rpc_param_descriptor **out_params,
word_t *num_params,
rpc_invoke_error *err);
* rpc_param_get_short
* 取得短整數參數值
void rpc_param_get_short (rpc_param_descriptor *param, SHORT *val);
* rpc_param_get_ushort
void rpc_param_get_ushort (rpc_param_descriptor *param, USHORT *val);
* rpc_param_get_long
* 取得整數參數值
void rpc_param_get_long (rpc_param_descriptor *param, LONG *val);
* rpc_param_get_ulong
* 取得無符号整數參數值
void rpc_param_get_ulong (rpc_param_descriptor *param, ULONG *val);
* rpc_param_get_int
void rpc_param_get_int (rpc_param_descriptor *param, INT *val);
* rpc_param_get_double
* 取得雙精度參數值
void rpc_param_get_double (rpc_param_descriptor *param, double *val);
* rpc_invoke_send
* 發送資料
RPCRESULT rpc_invoke_send(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_error *err);
* rpc_invoke_recv
* 接收資料
RPCRESULT rpc_invoke_recv(rpc_connection_descriptor *conn, rpc_invoke_descriptor *outv, rpc_invoke_error *err);
* 執行RPC調用
RPCRESULT rpc_invoke_execute(rpc_connection_descriptor *conn,
rpc_invoke_descriptor *inv,
rpc_invoke_descriptor *outv,
rpc_invoke_error *err);
#endif /* _RPCAPI_H__ */
rpcapi.c如下:
* rpcapi.c [email protected]
#include "rpcapi.h"
// 打開socket
static RPCRESULT opensocket(SOCKET *pStream, const char *lpszServer, int nPort, rpc_invoke_error *pError)
struct sockaddr_in server;
struct hostent *hp = 0;
unsigned int iAddr;
*pStream = INVALID_SOCKET;
// Parse server host
if(inet_addr(lpszServer) == INADDR_NONE){
hp = gethostbyname(lpszServer);
if(hp==0)
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_HOST, "opensocket()", pError);
server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
else{
iAddr = inet_addr(lpszServer);
server.sin_addr.s_addr = iAddr;
// too slow to use gethostbyaddr
//OLD: hp = gethostbyaddr((char*)&iAddr, sizeof(iAddr), AF_INET);
//OLD: if(hp==0)
//OLD: return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_HOST, "opensocket()", pError);
//OLD: server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
server.sin_family = AF_INET;
server.sin_port = htons((u_short)nPort);
// Create a new socket and attempt to connect to server
(*pStream) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ((*pStream) == INVALID_SOCKET)
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "socket()", pError);
// Make a connection to the server
if (connect((*pStream), (struct sockaddr *) &server, sizeof(server))==SOCKET_ERROR){
closesocket( *pStream );
*pStream = INVALID_SOCKET;
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "connect()", pError);
setbufsize(*pStream, RPC_BUFFSIZE, RPC_BUFFSIZE);
return RPC_SUCCESS;
// 列集參數
static int rpc_param_marshal(rpc_param_descriptor *param, char *buf)
word_t w;
dword_t dw;
// 參數頭RPC_PARAM_HEADER_SIZE位元組:
// [參數類型(2)|類型位元組(2)|數組元素數目(4)|參數資料位元組數(4)|參數成員數(2)=0|保留(2)=0]
w = word_hton(param->param_type);
w = word_hton(param->size_type);
dw = dword_hton((dword_t)param->array_size);
memcpy(buf+pos, &dw, 4);
dw = dword_hton((dword_t)param->param_size);
w = 0;
// 參數的資料
memcpy(buf+pos, param->param_bytes, param->param_size);
pos += (int)param->param_size;
return pos;
// 散集參數
// 參數頭RPC_PARAM_HEADER_SIZE位元組:
// [參數類型(2)|類型位元組(2)|數組元素數目(4)|參數資料位元組數(4)|參數成員數(2)|保留(2)=0]
static size_t rpc_param_unmarshal(BOOL marshal_bigendian, rpc_param_descriptor *param, char *buf)
byte *pcb;
size_t i;
size_t pos = 0;
assert(param->array_size==0||param->array_size==param->param_size/param->size_type);
// 參數類型
param->param_type = word_ntoh(*((word_t*)(buf+pos)));
param->size_type = word_ntoh(*((word_t*)(buf+pos)));
param->array_size = dword_ntoh(*((dword_t*)(buf+pos)));
param->param_size = dword_ntoh(*((dword_t*)(buf+pos)));
// 參數成員
// 保留值
// 參數位元組指針
// 這裡假設列集方marshal_bigendian和散集方_rpc_is_bigendian的位元組次序一緻,
// 以後要添加代碼處理不一緻的情況
if (param->param_size==0){
param->param_bytes = 0;
return pos;
if (param->size_type==1 || marshal_bigendian==_rpc_is_bigendian){
param->param_bytes = (void*)(buf+pos);
return (pos + param->param_size);
// 必須交換位元組次序
assert(param->size_type==2||param->size_type==4||param->size_type==8);
assert(marshal_bigendian != _rpc_is_bigendian);
param->param_bytes = (void*)(buf+pos);
pcb = (buf+pos);
i = param->param_size/param->size_type;
while(i-->0){
swap_bytes(&pcb[i*param->size_type], param->size_type);
assert(i==-1);
return (pos + param->param_size);
void rpc_param_get_ushort (rpc_param_descriptor *param, USHORT *val)
assert(param && param->param_bytes && param->array_size==0 &&
param->param_type==RPCT_USHORT && param->size_type==RPC_TYPE_SIZE(RPCT_USHORT));
*val = *((USHORT*)(param->param_bytes));
void rpc_param_get_short (rpc_param_descriptor *param, SHORT *val)
param->param_type==RPCT_SHORT && param->size_type==RPC_TYPE_SIZE(RPCT_SHORT));
*val = *((SHORT*)(param->param_bytes));
void rpc_param_get_long (rpc_param_descriptor *param, LONG *val)
param->param_type==RPCT_LONG && param->size_type==RPC_TYPE_SIZE(RPCT_LONG));
*val = *((LONG*)(param->param_bytes));
void rpc_param_get_int (rpc_param_descriptor *param, INT *val)
param->param_type==RPCT_INT && param->size_type==RPC_TYPE_SIZE(RPCT_INT));
*val = *((INT*)(param->param_bytes));
void rpc_param_get_ulong (rpc_param_descriptor *param, ULONG *val)
param->param_type==RPCT_ULONG && param->size_type==RPC_TYPE_SIZE(RPCT_ULONG));
*val = *((ULONG*)(param->param_bytes));
void rpc_param_get_double (rpc_param_descriptor *param, DOUBLE *val)
param->param_type==RPCT_DOUBLE && param->size_type==RPC_TYPE_SIZE(RPCT_DOUBLE));
*val = *((DOUBLE*)(param->param_bytes));
RPCRESULT rpc_invoke_send(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_error *err)
BOOL br;
char *buffer = 0;
dword_t cbTotal = 0;
cbTotal = rpc_invoke_get_size(inv);
// 既然<=RPC_BUFFSIZE, 則使用預設的buffer
if (cbTotal <= RPC_BUFFSIZE){
cbTotal = rpc_invoke_marshal(inv, conn->buffer, cbTotal);
assert(cbTotal != -1);
if (cbTotal != sendmsg(conn->stream, conn->buffer, cbTotal, 0))
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "rpc_invoke_send()", err);
else
return RPC_SUCCESS;
else {
buffer = malloc(cbTotal);
// 記憶體配置設定失敗
if (!buffer)
return rpc_throw_error(RPC_SYSTEM_ERROR, GetLastError(), "rpc_invoke_send()", err);
cbTotal = rpc_invoke_marshal(inv, buffer, cbTotal);
// 發送大塊資料
br = sendbulk(conn->stream, buffer, cbTotal, 0, RPC_BUFFSIZE);
FREE_S(buffer)
if (!br)
RPCRESULT rpc_invoke_recv(rpc_connection_descriptor *conn, rpc_invoke_descriptor *outv, rpc_invoke_error *err)
RPCRESULT res;
// 接收頭
res = rpc_invokehdr_unmarshal(conn->stream, conn->token, conn->buffer, RPC_INVOKE_HEADER_SIZE, outv, err);
if (RPC_SUCCESS != res)
return res;
// 驗證token
if (conn->token != outv->invToken)
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "rpc_invoke_recv()", err);
// 接收其餘全部資料
if (outv->totalBytes > RPC_BUFFSIZE){
MALLOC_S(outv->pbBuf, outv->totalBytes, char);
memcpy(outv->pbBuf, conn->buffer, RPC_INVOKE_HEADER_SIZE);
outv->pbBuf = conn->buffer;
assert(outv->pbBuf);
if (!recvbulk(conn->stream, outv->pbBuf+RPC_INVOKE_HEADER_SIZE, outv->totalBytes-RPC_INVOKE_HEADER_SIZE, 0, RPC_BUFFSIZE)){
if (outv->pbBuf != conn->buffer){
FREE_S(outv->pbBuf)
outv->pbBuf = 0;
}
return rpc_throw_error(RPC_USER_ERROR, RPC_NETWORK_ERROR, "rpc_invoke_recv()", err);
if (RPC_SUCCESS != rpc_invoke_unmarshal(outv, outv->pbBuf, outv->totalBytes, &outv->params_list, &outv->num_params, err)){
Public Functions
Remote Procedure Call Impementation
rpc_invoke_descriptor*
rpc_invoke_create(rpc_invoke_descriptor **inv, ushort ordinal, ushort num_params)
{
assert(inv);
MALLOC_S(*inv, 1, rpc_invoke_descriptor)
assert(*inv);
(*inv)->bigEndian = _rpc_is_bigendian;
(*inv)->ordinal = ordinal;
(*inv)->num_params = num_params;
MALLOC_S((*inv)->params_list, num_params, rpc_param_descriptor)
return (*inv);
rpc_invoke_init(rpc_invoke_descriptor *inv, ushort ordinal, ushort num_params)
rpc_invoke_clear_all(inv);
inv->ordinal = ordinal;
inv->num_params = num_params;
MALLOC_S(inv->params_list, num_params, rpc_param_descriptor)
return inv;
void rpc_invoke_clear_all(rpc_invoke_descriptor *inv)
if (inv->pbBuf && inv->totalBytes>RPC_BUFFSIZE){
FREE_S(inv->pbBuf)
assert(inv->pbBuf==0);
rpc_param_free_list(inv->params_list, inv->num_params);
memset(inv, 0, sizeof(rpc_invoke_descriptor));
inv->bigEndian = _rpc_is_bigendian? 1:0;
void rpc_invoke_free(rpc_invoke_descriptor *inv)
FREE_S(inv)
rpc_param_descriptor* rpc_invoke_bind_param(rpc_invoke_descriptor *inv, ushort id, word_t type, void *vaddr, size_t array_size, BOOL is_owner)
rpc_param_descriptor* p;
assert(id>=0 && id<inv->num_params);
p = &inv->params_list[id];
p->param_type = type;
p->size_type = RPC_TYPE_SIZE(type);
p->array_size = array_size;
p->param_bytes = vaddr; // may be NULL
if (type == RPCT_STRING)
p->param_size = array_size;
else
p->param_size = p->size_type * (array_size > 0? array_size : 1);
p->is_owner = is_owner;
return p;
void rpc_param_clear(rpc_param_descriptor *p)
if (p->is_owner)
FREE_S(p->param_bytes)
memset(p, 0, sizeof(rpc_param_descriptor));
void rpc_param_free_list(rpc_param_descriptor *param_list, word_t num_params)
while(num_params-->0)
rpc_param_clear(&param_list[num_params]);
FREE_S(param_list)
RPCRESULT rpc_connection_create(rpc_connection_descriptor **conn, const char *host, const char *port, long rcvtimeo, rpc_invoke_error *err)
WSADATA wsd;
struct timeval tv_out;
int i;
// 加載WinSock DLL
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
return rpc_throw_error(RPC_USER_ERROR, RPC_WINSOCK_NOTFOUND, "WSAStartup()", err);
// 建立對象
MALLOC_S(*conn, 1, rpc_connection_descriptor)
// 設定位元組順序
(*conn)->is_bigendian = _rpc_is_bigendian;
// 設定主機名
if (!host || host[0]==0)
strcpy_s((*conn)->host, 128, "localhost");
else if (strlen(host)<128)
strcpy_s((*conn)->host, 128, host);
rpc_connection_free(*conn);
return rpc_throw_error(RPC_USER_ERROR, RPC_HOSTNAME_TOOLONG, "rpc_connection_create()", err);
// 設定端口号:
// 必須是 gde:xxxx
if (!port || port[0]==0 || strlen(port)<4 || strlen(port)>5) {
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_PORT, "rpc_connection_create()", err);
i = 0;
while( port[i] != 0 ){
if ( !isdigit(port[i]) ){
rpc_connection_free(*conn);
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_PORT, "rpc_connection_create()", err);
i++;
i = atoi(port);
if (i<0x0400 || i>0xFFFF) { // port = [1024,65535]
return rpc_throw_error(RPC_USER_ERROR, RPC_PORTNUM_OUTOF_SCOPE, "rpc_connection_create()", err);
strcpy_s((*conn)->port, 6, port);
// 打開SOCKET
if (RPC_SUCCESS != opensocket(&((*conn)->stream), host, i, err)){
return RPC_ERROR;
/*
* set timeout for recv */
if (rcvtimeo >= 0){
tv_out.tv_sec = rcvtimeo/1000;
tv_out.tv_usec = (rcvtimeo%1000)*1000;
i = setsockopt((*conn)->stream, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_out, sizeof(tv_out));
assert(i == 0);
(*conn)->timeout = rcvtimeo;
void rpc_connection_free(rpc_connection_descriptor *conn)
if (conn){
closesocket(conn->stream);
WSACleanup();
FREE_S(conn)
void rpc_connection_set(rpc_connection_descriptor *conn, dword_t token, dword_t timeout, byte enc_hdr, byte enc_pkg)
conn->token = token;
conn->timeout = timeout;
conn->enc_hdr = enc_hdr;
conn->enc_pkg = enc_pkg;
// 計算參數位元組總數
dword_t rpc_param_get_size(rpc_param_descriptor *param)
return RPC_PARAM_HEADER_SIZE + (dword_t)param->param_size;
// 計算調用消息的位元組總數
dword_t rpc_invoke_get_size(rpc_invoke_descriptor *inv)
ushort i;
dword_t cbTotal = RPC_INVOKE_HEADER_SIZE;
// 列集頭24位元組:
// ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
// 計算列集參數位元組
for (i=0; i<inv->num_params; i++)
cbTotal += rpc_param_get_size(&(inv->params_list[i]));
return cbTotal;
// 把目前調用的資料全部放到totalBuf中, 傳回全部位元組數
/* 24位元組頭 + 内容
* ['RCmn'(4)|使用者token(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
int rpc_invoke_marshal(rpc_invoke_descriptor *inv, char *totalBuf, int totalSize)
dword_t pos;
char key[33];
SET_BIT(inv->bitsFlag, RPC_BIGENDIAN_BIT, inv->bigEndian);
SET_BIT(inv->bitsFlag, RPC_ENCHEADER_BIT, inv->encHdr==0? 0:1);
SET_BIT(inv->bitsFlag, RPC_ENCPACKAGE_BIT, inv->encPkg==0? 0:1);
// 列集方法頭
totalBuf[0] = 'R';
totalBuf[1] = 'C';
totalBuf[2] = inv->encPkg;
totalBuf[3] = inv->encHdr;
pos = 4;
dw = dword_hton(inv->invToken);
memcpy(totalBuf+pos, &dw, 4);
dw = dword_hton(totalSize);
w = word_hton(inv->bitsFlag);
memcpy(totalBuf+pos, &w, 2);
dw = dword_hton(inv->result);
w = word_hton(inv->ordinal);
w = word_hton(inv->num_params);
assert(pos==RPC_INVOKE_HEADER_SIZE);
// 列集每個參數
for (w=0; w<inv->num_params; w++){
assert((int)pos<=totalSize);
pos += rpc_param_marshal(&inv->params_list[w], totalBuf+pos);
// 加密包資料
if (inv->encPkg != 0){
dword_t dw = inv->encPkg;
srand(dw);
MD5_hash_string(totalBuf, RPC_INVOKE_HEADER_SIZE, (dw<<24)+rand(), key);
RC4_encrypt_string(totalBuf+RPC_INVOKE_HEADER_SIZE, totalSize-RPC_INVOKE_HEADER_SIZE, key+8, 16);
// 加密消息頭
if (inv->encHdr != 0){
dw = inv->encHdr;
MD5_hash_string(totalBuf, 8, (dw<<16)+rand(), key);
RC4_encrypt_string(totalBuf+4, RPC_INVOKE_HEADER_SIZE-4, key, 16);
return pos;
/**
* 散集調用頭
RPCRESULT rpc_invokehdr_unmarshal(SOCKET sClient, dword_t dwToken, char *rpcHdr, int hdrSize, rpc_invoke_descriptor *inv, rpc_invoke_error *err)
/**
* Perform a blocking recv() call
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)] */
assert(hdrSize == RPC_INVOKE_HEADER_SIZE);
if (RPC_INVOKE_HEADER_SIZE != recvmsg(sClient, rpcHdr, RPC_INVOKE_HEADER_SIZE, 0))
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "RecvRpcCallHeader", err);
// 讀調用聲明 = "RPCn", n為0-255的密鑰種子. 0表示不加密; !=0表示RC4加密的密鑰種子
if (rpcHdr[0]!='R'||rpcHdr[1]!='C')
inv->encPkg = rpcHdr[2];
inv->encHdr = rpcHdr[3];
dword_t dw;
char hdr[41];
hdr[0]=rpcHdr[0];
hdr[1]=rpcHdr[1];
hdr[2]=rpcHdr[2];
hdr[3]=rpcHdr[3];
dw = dword_hton(dwToken);
memcpy(&hdr[4], &dw, 4);
dw=inv->encHdr;
MD5_hash_string(hdr, 8, (dw<<16)+rand(), hdr+8);
RC4_encrypt_string(rpcHdr+4, RPC_INVOKE_HEADER_SIZE-4, hdr+8, 16);
inv->invToken = dword_ntoh(*((dword_t*)(rpcHdr+4))); // 使用者辨別
inv->totalBytes = dword_ntoh(*((dword_t*)(rpcHdr+8))); // 總消息位元組數(也許是壓縮的)
inv->bitsFlag = word_ntoh(*((word_t*)(rpcHdr+12))); // 标志
inv->bigEndian = GET_BIT(inv->bitsFlag, RPC_BIGENDIAN_BIT); // 客戶方列集的位元組次序
inv->reserved = word_ntoh(*((word_t*)(rpcHdr+14))); // 保留值
inv->result = dword_ntoh(*((dword_t*)(rpcHdr+16))); // 傳回值
inv->ordinal = word_ntoh(*((word_t*)(rpcHdr+20))); // 方法序号
inv->num_params = word_ntoh(*((word_t*)(rpcHdr+22))); // 參數數目
/* 散集資料到參數中, 資料頭總是不壓縮的. 參數資料總是先壓縮後加密的
* 24位元組頭 + 内容
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
char *totalBuf,
size_t totalSize,
word_t *num_params,
rpc_invoke_error *err)
ushort i;
size_t pos;
rpc_param_descriptor *params_list;
assert(totalSize >= RPC_INVOKE_HEADER_SIZE);
// 讀調用聲明 = "RCmn", m,n為0-255的密鑰種子. 0表示不加密; !=0表示RC4加密的密鑰種子
assert(totalBuf[0]=='R'&&totalBuf[1]=='C');
// 解密包資料
char key[33];
pos = RPC_INVOKE_HEADER_SIZE;
// 配置設定參數數組
MALLOC_S(params_list, inv->num_params, rpc_param_descriptor)
for (i=0; i<inv->num_params; i++){
pos += rpc_param_unmarshal(inv->bigEndian, &params_list[i], totalBuf+pos);
}
*out_params = params_list;
*num_params = inv->num_params;
assert(pos == totalSize);
RPCRESULT rpc_invoke_execute(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_descriptor *outv, rpc_invoke_error* err)
inv->encHdr = conn->enc_hdr;
inv->encPkg = conn->enc_pkg;
inv->invToken = conn->token;
inv->bigEndian = conn->is_bigendian? 1:0;
if (RPC_SUCCESS != rpc_invoke_send(conn, inv, err)){
assert(0);
return err->err_code;
if (RPC_SUCCESS != rpc_invoke_recv(conn, outv, err))
* rpcapi.c [email protected]
#include "rpcapi.h"
// 打開socket
static RPCRESULT opensocket(SOCKET *pStream, const char *lpszServer, int nPort, rpc_invoke_error *pError)
struct sockaddr_in server;
struct hostent *hp = 0;
unsigned int iAddr;
*pStream = INVALID_SOCKET;
// Parse server host
if(inet_addr(lpszServer) == INADDR_NONE){
hp = gethostbyname(lpszServer);
if(hp==0)
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_HOST, "opensocket()", pError);
server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
else{
iAddr = inet_addr(lpszServer);
server.sin_addr.s_addr = iAddr;
// too slow to use gethostbyaddr
//OLD: hp = gethostbyaddr((char*)&iAddr, sizeof(iAddr), AF_INET);
//OLD: if(hp==0)
//OLD: return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_HOST, "opensocket()", pError);
//OLD: server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
server.sin_family = AF_INET;
server.sin_port = htons((u_short)nPort);
// Create a new socket and attempt to connect to server
(*pStream) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if ((*pStream) == INVALID_SOCKET)
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "socket()", pError);
// Make a connection to the server
if (connect((*pStream), (struct sockaddr *) &server, sizeof(server))==SOCKET_ERROR){
closesocket( *pStream );
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "connect()", pError);
setbufsize(*pStream, RPC_BUFFSIZE, RPC_BUFFSIZE);
return RPC_SUCCESS;
// 列集參數
static int rpc_param_marshal(rpc_param_descriptor *param, char *buf)
word_t w;
dword_t dw;
// 參數頭RPC_PARAM_HEADER_SIZE位元組:
// [參數類型(2)|類型位元組(2)|數組元素數目(4)|參數資料位元組數(4)|參數成員數(2)=0|保留(2)=0]
w = word_hton(param->param_type);
w = word_hton(param->size_type);
dw = dword_hton((dword_t)param->array_size);
memcpy(buf+pos, &dw, 4);
dw = dword_hton((dword_t)param->param_size);
w = 0;
// 參數的資料
memcpy(buf+pos, param->param_bytes, param->param_size);
pos += (int)param->param_size;
return pos;
// 散集參數
// [參數類型(2)|類型位元組(2)|數組元素數目(4)|參數資料位元組數(4)|參數成員數(2)|保留(2)=0]
static size_t rpc_param_unmarshal(BOOL marshal_bigendian, rpc_param_descriptor *param, char *buf)
byte *pcb;
size_t i;
size_t pos = 0;
assert(param->array_size==0||param->array_size==param->param_size/param->size_type);
// 參數類型
param->param_type = word_ntoh(*((word_t*)(buf+pos)));
param->size_type = word_ntoh(*((word_t*)(buf+pos)));
param->array_size = dword_ntoh(*((dword_t*)(buf+pos)));
param->param_size = dword_ntoh(*((dword_t*)(buf+pos)));
// 參數成員
// 保留值
// 參數位元組指針
// 這裡假設列集方marshal_bigendian和散集方_rpc_is_bigendian的位元組次序一緻,
// 以後要添加代碼處理不一緻的情況
if (param->param_size==0){
param->param_bytes = 0;
if (param->size_type==1 || marshal_bigendian==_rpc_is_bigendian){
param->param_bytes = (void*)(buf+pos);
return (pos + param->param_size);
// 必須交換位元組次序
assert(param->size_type==2||param->size_type==4||param->size_type==8);
assert(marshal_bigendian != _rpc_is_bigendian);
pcb = (buf+pos);
i = param->param_size/param->size_type;
while(i-->0){
swap_bytes(&pcb[i*param->size_type], param->size_type);
assert(i==-1);
void rpc_param_get_ushort (rpc_param_descriptor *param, USHORT *val)
assert(param && param->param_bytes && param->array_size==0 &&
param->param_type==RPCT_USHORT && param->size_type==RPC_TYPE_SIZE(RPCT_USHORT));
*val = *((USHORT*)(param->param_bytes));
void rpc_param_get_short (rpc_param_descriptor *param, SHORT *val)
param->param_type==RPCT_SHORT && param->size_type==RPC_TYPE_SIZE(RPCT_SHORT));
*val = *((SHORT*)(param->param_bytes));
void rpc_param_get_long (rpc_param_descriptor *param, LONG *val)
param->param_type==RPCT_LONG && param->size_type==RPC_TYPE_SIZE(RPCT_LONG));
*val = *((LONG*)(param->param_bytes));
void rpc_param_get_int (rpc_param_descriptor *param, INT *val)
param->param_type==RPCT_INT && param->size_type==RPC_TYPE_SIZE(RPCT_INT));
*val = *((INT*)(param->param_bytes));
void rpc_param_get_ulong (rpc_param_descriptor *param, ULONG *val)
param->param_type==RPCT_ULONG && param->size_type==RPC_TYPE_SIZE(RPCT_ULONG));
*val = *((ULONG*)(param->param_bytes));
void rpc_param_get_double (rpc_param_descriptor *param, DOUBLE *val)
param->param_type==RPCT_DOUBLE && param->size_type==RPC_TYPE_SIZE(RPCT_DOUBLE));
*val = *((DOUBLE*)(param->param_bytes));
RPCRESULT rpc_invoke_send(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_error *err)
BOOL br;
char *buffer = 0;
dword_t cbTotal = 0;
cbTotal = rpc_invoke_get_size(inv);
// 既然<=RPC_BUFFSIZE, 則使用預設的buffer
if (cbTotal <= RPC_BUFFSIZE){
cbTotal = rpc_invoke_marshal(inv, conn->buffer, cbTotal);
assert(cbTotal != -1);
if (cbTotal != sendmsg(conn->stream, conn->buffer, cbTotal, 0))
return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "rpc_invoke_send()", err);
else
else {
buffer = malloc(cbTotal);
// 記憶體配置設定失敗
if (!buffer)
return rpc_throw_error(RPC_SYSTEM_ERROR, GetLastError(), "rpc_invoke_send()", err);
cbTotal = rpc_invoke_marshal(inv, buffer, cbTotal);
// 發送大塊資料
br = sendbulk(conn->stream, buffer, cbTotal, 0, RPC_BUFFSIZE);
FREE_S(buffer)
if (!br)
RPCRESULT rpc_invoke_recv(rpc_connection_descriptor *conn, rpc_invoke_descriptor *outv, rpc_invoke_error *err)
RPCRESULT res;
// 接收頭
res = rpc_invokehdr_unmarshal(conn->stream, conn->token, conn->buffer, RPC_INVOKE_HEADER_SIZE, outv, err);
if (RPC_SUCCESS != res)
return res;
// 驗證token
if (conn->token != outv->invToken)
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "rpc_invoke_recv()", err);
// 接收其餘全部資料
if (outv->totalBytes > RPC_BUFFSIZE){
MALLOC_S(outv->pbBuf, outv->totalBytes, char);
memcpy(outv->pbBuf, conn->buffer, RPC_INVOKE_HEADER_SIZE);
outv->pbBuf = conn->buffer;
assert(outv->pbBuf);
if (!recvbulk(conn->stream, outv->pbBuf+RPC_INVOKE_HEADER_SIZE, outv->totalBytes-RPC_INVOKE_HEADER_SIZE, 0, RPC_BUFFSIZE)){
if (outv->pbBuf != conn->buffer){
FREE_S(outv->pbBuf)
outv->pbBuf = 0;
return rpc_throw_error(RPC_USER_ERROR, RPC_NETWORK_ERROR, "rpc_invoke_recv()", err);
if (RPC_SUCCESS != rpc_invoke_unmarshal(outv, outv->pbBuf, outv->totalBytes, &outv->params_list, &outv->num_params, err)){
Public Functions
Remote Procedure Call Impementation
rpc_invoke_descriptor*
rpc_invoke_create(rpc_invoke_descriptor **inv, ushort ordinal, ushort num_params)
assert(inv);
MALLOC_S(*inv, 1, rpc_invoke_descriptor)
assert(*inv);
(*inv)->bigEndian = _rpc_is_bigendian;
(*inv)->ordinal = ordinal;
(*inv)->num_params = num_params;
MALLOC_S((*inv)->params_list, num_params, rpc_param_descriptor)
return (*inv);
rpc_invoke_init(rpc_invoke_descriptor *inv, ushort ordinal, ushort num_params)
rpc_invoke_clear_all(inv);
inv->ordinal = ordinal;
inv->num_params = num_params;
MALLOC_S(inv->params_list, num_params, rpc_param_descriptor)
return inv;
void rpc_invoke_clear_all(rpc_invoke_descriptor *inv)
if (inv->pbBuf && inv->totalBytes>RPC_BUFFSIZE){
FREE_S(inv->pbBuf)
assert(inv->pbBuf==0);
rpc_param_free_list(inv->params_list, inv->num_params);
memset(inv, 0, sizeof(rpc_invoke_descriptor));
inv->bigEndian = _rpc_is_bigendian? 1:0;
void rpc_invoke_free(rpc_invoke_descriptor *inv)
FREE_S(inv)
rpc_param_descriptor* rpc_invoke_bind_param(rpc_invoke_descriptor *inv, ushort id, word_t type, void *vaddr, size_t array_size, BOOL is_owner)
rpc_param_descriptor* p;
assert(id>=0 && id<inv->num_params);
p = &inv->params_list[id];
p->param_type = type;
p->size_type = RPC_TYPE_SIZE(type);
p->array_size = array_size;
p->param_bytes = vaddr; // may be NULL
if (type == RPCT_STRING)
p->param_size = array_size;
p->param_size = p->size_type * (array_size > 0? array_size : 1);
p->is_owner = is_owner;
return p;
void rpc_param_clear(rpc_param_descriptor *p)
if (p->is_owner)
FREE_S(p->param_bytes)
memset(p, 0, sizeof(rpc_param_descriptor));
void rpc_param_free_list(rpc_param_descriptor *param_list, word_t num_params)
while(num_params-->0)
rpc_param_clear(&param_list[num_params]);
FREE_S(param_list)
RPCRESULT rpc_connection_create(rpc_connection_descriptor **conn, const char *host, const char *port, long rcvtimeo, rpc_invoke_error *err)
WSADATA wsd;
struct timeval tv_out;
int i;
// 加載WinSock DLL
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
return rpc_throw_error(RPC_USER_ERROR, RPC_WINSOCK_NOTFOUND, "WSAStartup()", err);
// 建立對象
MALLOC_S(*conn, 1, rpc_connection_descriptor)
// 設定位元組順序
(*conn)->is_bigendian = _rpc_is_bigendian;
// 設定主機名
if (!host || host[0]==0)
strcpy_s((*conn)->host, 128, "localhost");
else if (strlen(host)<128)
strcpy_s((*conn)->host, 128, host);
rpc_connection_free(*conn);
return rpc_throw_error(RPC_USER_ERROR, RPC_HOSTNAME_TOOLONG, "rpc_connection_create()", err);
// 設定端口号:
// 必須是 gde:xxxx
if (!port || port[0]==0 || strlen(port)<4 || strlen(port)>5) {
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALID_PORT, "rpc_connection_create()", err);
i = 0;
while( port[i] != 0 ){
if ( !isdigit(port[i]) ){
i++;
i = atoi(port);
if (i<0x0400 || i>0xFFFF) { // port = [1024,65535]
return rpc_throw_error(RPC_USER_ERROR, RPC_PORTNUM_OUTOF_SCOPE, "rpc_connection_create()", err);
strcpy_s((*conn)->port, 6, port);
// 打開SOCKET
if (RPC_SUCCESS != opensocket(&((*conn)->stream), host, i, err)){
return RPC_ERROR;
/*
* set timeout for recv */
if (rcvtimeo >= 0){
tv_out.tv_sec = rcvtimeo/1000;
tv_out.tv_usec = (rcvtimeo%1000)*1000;
i = setsockopt((*conn)->stream, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_out, sizeof(tv_out));
assert(i == 0);
(*conn)->timeout = rcvtimeo;
void rpc_connection_free(rpc_connection_descriptor *conn)
if (conn){
closesocket(conn->stream);
WSACleanup();
FREE_S(conn)
void rpc_connection_set(rpc_connection_descriptor *conn, dword_t token, dword_t timeout, byte enc_hdr, byte enc_pkg)
conn->token = token;
conn->timeout = timeout;
conn->enc_hdr = enc_hdr;
conn->enc_pkg = enc_pkg;
// 計算參數位元組總數
dword_t rpc_param_get_size(rpc_param_descriptor *param)
return RPC_PARAM_HEADER_SIZE + (dword_t)param->param_size;
// 計算調用消息的位元組總數
dword_t rpc_invoke_get_size(rpc_invoke_descriptor *inv)
ushort i;
dword_t cbTotal = RPC_INVOKE_HEADER_SIZE;
// 列集頭24位元組:
// ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
// 計算列集參數位元組
for (i=0; i<inv->num_params; i++)
cbTotal += rpc_param_get_size(&(inv->params_list[i]));
return cbTotal;
// 把目前調用的資料全部放到totalBuf中, 傳回全部位元組數
/* 24位元組頭 + 内容
* ['RCmn'(4)|使用者token(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
int rpc_invoke_marshal(rpc_invoke_descriptor *inv, char *totalBuf, int totalSize)
dword_t pos;
char key[33];
SET_BIT(inv->bitsFlag, RPC_BIGENDIAN_BIT, inv->bigEndian);
SET_BIT(inv->bitsFlag, RPC_ENCHEADER_BIT, inv->encHdr==0? 0:1);
SET_BIT(inv->bitsFlag, RPC_ENCPACKAGE_BIT, inv->encPkg==0? 0:1);
// 列集方法頭
totalBuf[0] = 'R';
totalBuf[1] = 'C';
totalBuf[2] = inv->encPkg;
totalBuf[3] = inv->encHdr;
pos = 4;
dw = dword_hton(inv->invToken);
memcpy(totalBuf+pos, &dw, 4);
dw = dword_hton(totalSize);
w = word_hton(inv->bitsFlag);
memcpy(totalBuf+pos, &w, 2);
dw = dword_hton(inv->result);
w = word_hton(inv->ordinal);
w = word_hton(inv->num_params);
assert(pos==RPC_INVOKE_HEADER_SIZE);
// 列集每個參數
for (w=0; w<inv->num_params; w++){
assert((int)pos<=totalSize);
pos += rpc_param_marshal(&inv->params_list[w], totalBuf+pos);
// 加密包資料
if (inv->encPkg != 0){
dword_t dw = inv->encPkg;
srand(dw);
MD5_hash_string(totalBuf, RPC_INVOKE_HEADER_SIZE, (dw<<24)+rand(), key);
RC4_encrypt_string(totalBuf+RPC_INVOKE_HEADER_SIZE, totalSize-RPC_INVOKE_HEADER_SIZE, key+8, 16);
// 加密消息頭
if (inv->encHdr != 0){
dw = inv->encHdr;
MD5_hash_string(totalBuf, 8, (dw<<16)+rand(), key);
RC4_encrypt_string(totalBuf+4, RPC_INVOKE_HEADER_SIZE-4, key, 16);
* 散集調用頭
RPCRESULT rpc_invokehdr_unmarshal(SOCKET sClient, dword_t dwToken, char *rpcHdr, int hdrSize, rpc_invoke_descriptor *inv, rpc_invoke_error *err)
* Perform a blocking recv() call
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)] */
assert(hdrSize == RPC_INVOKE_HEADER_SIZE);
if (RPC_INVOKE_HEADER_SIZE != recvmsg(sClient, rpcHdr, RPC_INVOKE_HEADER_SIZE, 0))
return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "RecvRpcCallHeader", err);
// 讀調用聲明 = "RPCn", n為0-255的密鑰種子. 0表示不加密; !=0表示RC4加密的密鑰種子
if (rpcHdr[0]!='R'||rpcHdr[1]!='C')
inv->encPkg = rpcHdr[2];
inv->encHdr = rpcHdr[3];
dword_t dw;
char hdr[41];
hdr[0]=rpcHdr[0];
hdr[1]=rpcHdr[1];
hdr[2]=rpcHdr[2];
hdr[3]=rpcHdr[3];
dw = dword_hton(dwToken);
memcpy(&hdr[4], &dw, 4);
dw=inv->encHdr;
MD5_hash_string(hdr, 8, (dw<<16)+rand(), hdr+8);
RC4_encrypt_string(rpcHdr+4, RPC_INVOKE_HEADER_SIZE-4, hdr+8, 16);
inv->invToken = dword_ntoh(*((dword_t*)(rpcHdr+4))); // 使用者辨別
inv->totalBytes = dword_ntoh(*((dword_t*)(rpcHdr+8))); // 總消息位元組數(也許是壓縮的)
inv->bitsFlag = word_ntoh(*((word_t*)(rpcHdr+12))); // 标志
inv->bigEndian = GET_BIT(inv->bitsFlag, RPC_BIGENDIAN_BIT); // 客戶方列集的位元組次序
inv->reserved = word_ntoh(*((word_t*)(rpcHdr+14))); // 保留值
inv->result = dword_ntoh(*((dword_t*)(rpcHdr+16))); // 傳回值
inv->ordinal = word_ntoh(*((word_t*)(rpcHdr+20))); // 方法序号
inv->num_params = word_ntoh(*((word_t*)(rpcHdr+22))); // 參數數目
/* 散集資料到參數中, 資料頭總是不壓縮的. 參數資料總是先壓縮後加密的
* 24位元組頭 + 内容
* ['RCmn'(4)|使用者辨別(4)|總消息位元組(4)|标志(2)| 保留(2)|傳回值(4)|方法序号(2)|參數數目(2)]
char *totalBuf,
size_t totalSize,
word_t *num_params,
rpc_invoke_error *err)
ushort i;
size_t pos;
rpc_param_descriptor *params_list;
assert(totalSize >= RPC_INVOKE_HEADER_SIZE);
// 讀調用聲明 = "RCmn", m,n為0-255的密鑰種子. 0表示不加密; !=0表示RC4加密的密鑰種子
assert(totalBuf[0]=='R'&&totalBuf[1]=='C');
// 解密包資料
char key[33];
pos = RPC_INVOKE_HEADER_SIZE;
// 配置設定參數數組
MALLOC_S(params_list, inv->num_params, rpc_param_descriptor)
for (i=0; i<inv->num_params; i++){
pos += rpc_param_unmarshal(inv->bigEndian, &params_list[i], totalBuf+pos);
*out_params = params_list;
*num_params = inv->num_params;
assert(pos == totalSize);
RPCRESULT rpc_invoke_execute(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_descriptor *outv, rpc_invoke_error* err)
inv->encHdr = conn->enc_hdr;
inv->encPkg = conn->enc_pkg;
inv->invToken = conn->token;
inv->bigEndian = conn->is_bigendian? 1:0;
if (RPC_SUCCESS != rpc_invoke_send(conn, inv, err)){
assert(0);
return err->err_code;
if (RPC_SUCCESS != rpc_invoke_recv(conn, outv, err))
整個工程的代碼随後上傳:
<a href="http://cheungmine.download.csdn.net/">http://cheungmine.download.csdn.net/</a>