用户自定义结构数据与VARIANT转换
cheungmine
将用户自定义的C结构数据存储成VARIANT类型,需要时再将VARIANT类型转为用户自定义的结构数据,有十分现实的意义,既然我们不想为这样的结构数据写一个COM包装类。虽然有很多方法和手段生成这样的VARIANT,但是,多数时候可能需要一个更加简单的,灵活的方法。我在做远程过程调用的C接口时,忽然联想到,既然RPC可以把任何数据以字节的形式发送,那么,就可以利用这个机制,把结构打包成字节数组。而字节数据是可以很方便地存储在VARIANT中。
这个过程是广为人知的,但是,真正把结构列集成字节数组,如果不想使用某些标称的序列化的方法,而全部自己写,的确要费一番功夫。不是
技术有多难,是很繁琐。我把前2年写的代码翻出来,简单调用一下,就有了这篇文章。采用我的方法,C/C++程序员可以把自己定义的结构放到VARIANT、CComVariant、COleVariant等各种VARIANT中,也可以反向转换。而VARIANT是可以很方便地在COM接口中传递。这样,就多了一种在自动化COM接口中传递自定义结构的手段。
不多说废话,全部内容见下面的代码,我还会上传整个工程。
struct2variant.cpp 如下:
/////////////////////////////////////////////////////////////////////// // struct2variant.cpp // cheungmine@gmail.com // 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的所有权(不负责释放内存) */ ); rpc_invoke_bind_param(&inv, 1, RPCT_STRING, (void*)inData->server, strlen(inData->server)+1, 0 ); rpc_invoke_bind_param(&inv, 2, RPCT_STRING, (void*)inData->instance, strlen(inData->instance)+1, 0 ); rpc_invoke_bind_param(&inv, 3, RPCT_STRING, (void*)inData->userid, strlen(inData->userid)+1, 0 ); rpc_invoke_bind_param(&inv, 4, RPCT_BOOL, (void*) &inData->isdraw, 0, /* 不是数组, 为0 */ 0 ); rpc_invoke_bind_param(&inv, 5, RPCT_ULONG, (void*) &inData->token, 0, 0 ); rpc_invoke_bind_param(&inv, 6, RPCT_LONG, (void*) &inData->timeout, 0, 0 ); rpc_invoke_bind_param(&inv, 7, RPCT_LONG, (void*) &inData->keepalive, 0, 0 ); rpc_invoke_bind_param(&inv, 8, RPCT_LONG, (void*) &inData->reserved, 0, 0 ); rpc_invoke_bind_param(&inv, 9, RPCT_BOOL, (void*) &inData->status, 0, 0 ); rpc_invoke_bind_param(&inv, 10, RPCT_LONG, (void*) &inData->capacity, 0, 0 ); rpc_invoke_bind_param(&inv, 11, RPCT_LONG, (void*) &inData->counter, 0, 0 ); rpc_invoke_bind_param(&inv, 12, RPCT_SHORT, (void*) &inData->numPts, 0, 0 ); rpc_invoke_bind_param(&inv, 13, RPCT_DOUBLE, /* 简单结构的成员类型 */ (void*) inData->ptArray, /* 内存结构=[x1,y1,x2,y2,...,xn,yn], 千万不可写成:(void*) &inData->ptArray */ inData->numPts * (sizeof(PointF)/sizeof(double)), /* double类型成员的数目=点数x2 */ 0 ); /* 计算调用消息的字节总数 */ dword_t cbOut = rpc_invoke_get_size(&inv); char *outBuf = (char*) malloc(cbOut); /* 列集全部数据到totalBuf中 */ rpc_invoke_marshal(&inv, outBuf, cbOut); ////////////////////////////////////////////////////////// // 到此,我们已经在outBuf中保存了结构的全部数据, // 下面把数据转换成IStream进而转换成VARIANT CComPtr<IStream> spStm; 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); rpc_invoke_clear_all(&inv); } 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 -cheungmine@gmail.com * 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" { #endif 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) { if (!_rpc_is_bigendian) net = swap_dword_type(net); return net; } static word_t word_hton(word_t host) { if (!_rpc_is_bigendian) host = swap_word_type(host); return host; } static word_t word_ntoh(word_t net) { if (!_rpc_is_bigendian) net = swap_word_type(net); return net; } static qword_t qword_hton(qword_t host) { if (!_rpc_is_bigendian) swap_bytes(&host, sizeof(qword_t)); return host; } static qword_t qword_ntoh(qword_t net) { if (!_rpc_is_bigendian) swap_bytes(&net, sizeof(qword_t)); return net; } static double double_hton(double host) { if (!_rpc_is_bigendian) swap_bytes(&host, sizeof(qword_t)); return host; } static double double_ntoh(double net) { if (!_rpc_is_bigendian) swap_bytes(&net, sizeof(qword_t)); return net; } static float float_hton(float host) { if (!_rpc_is_bigendian) swap_bytes(&host, sizeof(dword_t)); return host; } static float float_ntoh(float net) { if (!_rpc_is_bigendian) swap_bytes(&net, sizeof(dword_t)); return net; } /*============================================================================= Private Functions 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); if (ret==SOCKET_ERROR || ret==0) return ret; offset += ret; left -= ret; } assert(offset==buflen); return offset; } static BOOL sendbulk(SOCKET s, const char* data, int dataSize, int flags, int maxmsg) { int send, ret; int offset = 0; int left = dataSize; while (left > 0) { send = left>maxmsg? maxmsg:left; ret = sendmsg(s, &data[offset], send, flags); if (ret != send) return FALSE; offset += ret; left -= ret; } return TRUE; } static BOOL recvbulk(SOCKET s, char* buf, int recvlen, int flags, int maxmsg) { int recv, ret; int offset = 0; int left = recvlen; while (left > 0){ recv = left>maxmsg? maxmsg:left; ret = recvmsg(s, &buf[offset], recv, flags); if (ret != recv) return FALSE; offset += ret; left -= ret; } return TRUE; } 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); memcpy(buf+pos, &w, 2); pos += 2; w = word_hton((word_t)err_source_size); memcpy(buf+pos, &w, 2); 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, &w, 2); pos += 2; 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(); *err->err_source = 0; errmsg(err->err_code, err->err_detail, RPC_ERROR_STRING_LEN); return FALSE; } static void debug_out(const char *debug_file, const char *fmt, ...) { #ifdef _DEBUG 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); #endif } /*============================================================================ 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_invoke_free * 删除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_connection_free * 设置输入的参数, 返回参数指针 */ 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_connection_free * 计算参数字节总数 */ dword_t rpc_param_get_size(rpc_param_descriptor *param); /** * rpc_connection_free * 计算调用消息的字节总数 */ 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_connection_free * 执行RPC调用 */ RPCRESULT rpc_invoke_execute(rpc_connection_descriptor *conn, rpc_invoke_descriptor *inv, rpc_invoke_descriptor *outv, rpc_invoke_error *err); #ifdef __cplusplus } #endif #endif /* _RPCAPI_H__ */
rpcapi.c如下:
/** * rpcapi.c -cheungmine@gmail.com */ #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; int pos = 0; // 参数头RPC_PARAM_HEADER_SIZE字节: // [参数类型(2)|类型字节(2)|数组元素数目(4)|参数数据字节数(4)|参数成员数(2)=0|保留(2)=0] w = word_hton(param->param_type); memcpy(buf+pos, &w, 2); pos += 2; w = word_hton(param->size_type); memcpy(buf+pos, &w, 2); pos += 2; dw = dword_hton((dword_t)param->array_size); memcpy(buf+pos, &dw, 4); pos += 4; dw = dword_hton((dword_t)param->param_size); memcpy(buf+pos, &dw, 4); pos += 4; w = 0; memcpy(buf+pos, &w, 2); pos += 2; w = 0; memcpy(buf+pos, &w, 2); pos += 2; // 参数的数据 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))); pos += 2; param->size_type = word_ntoh(*((word_t*)(buf+pos))); pos += 2; param->array_size = dword_ntoh(*((dword_t*)(buf+pos))); pos += 4; param->param_size = dword_ntoh(*((dword_t*)(buf+pos))); pos += 4; // 参数成员 pos += 2; // 保留值 pos += 2; // 参数字节指针 // 这里假设列集方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) { assert(param && param->param_bytes && param->array_size==0 && 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) { assert(param && param->param_bytes && param->array_size==0 && 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) { assert(param && param->param_bytes && param->array_size==0 && 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) { assert(param && param->param_bytes && param->array_size==0 && 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) { assert(param && param->param_bytes && param->array_size==0 && 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); assert(cbTotal != -1); // 发送大块数据 br = sendbulk(conn->stream, buffer, cbTotal, 0, RPC_BUFFSIZE); FREE_S(buffer) if (!br) return rpc_throw_error(RPC_SOCKET_ERROR, WSAGetLastError(), "rpc_invoke_send()", err); else return RPC_SUCCESS; } } 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); } else{ 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)){ if (outv->pbBuf != conn->buffer){ FREE_S(outv->pbBuf) outv->pbBuf = 0; } return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "rpc_invoke_recv()", err); } return RPC_SUCCESS; } /*============================================================================= 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_descriptor * rpc_invoke_init(rpc_invoke_descriptor *inv, ushort ordinal, ushort num_params) { assert(inv); 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) { assert(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) { rpc_invoke_clear_all(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(¶m_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); else { 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) { rpc_connection_free(*conn); 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] rpc_connection_free(*conn); 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)){ rpc_connection_free(*conn); 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; return RPC_SUCCESS; } 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) { word_t w; dword_t dw; 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); pos += 4; dw = dword_hton(totalSize); memcpy(totalBuf+pos, &dw, 4); pos += 4; w = word_hton(inv->bitsFlag); memcpy(totalBuf+pos, &w, 2); pos += 2; w = 0; memcpy(totalBuf+pos, &w, 2); pos += 2; dw = dword_hton(inv->result); memcpy(totalBuf+pos, &dw, 4); pos += 4; w = word_hton(inv->ordinal); memcpy(totalBuf+pos, &w, 2); pos += 2; w = word_hton(inv->num_params); memcpy(totalBuf+pos, &w, 2); pos += 2; 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; srand(dw); 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') return rpc_throw_error(RPC_USER_ERROR, RPC_INVALIDMSG, "RecvRpcCallHeader", err); inv->encPkg = rpcHdr[2]; inv->encHdr = rpcHdr[3]; if (inv->encHdr != 0){ 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; srand(dw); 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))); // 参数数目 return RPC_SUCCESS; } /* 散集数据到参数中, 数据头总是不压缩的. 参数数据总是先压缩后加密的 * 24字节头 + 内容 * ['RCmn'(4)|用户标识(4)|总消息字节(4)|标志(2)| 保留(2)|返回值(4)|方法序号(2)|参数数目(2)] */ 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) { 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'); // 解密包数据 if (inv->encPkg != 0){ char key[33]; 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); } 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, ¶ms_list[i], totalBuf+pos); } *out_params = params_list; *num_params = inv->num_params; assert(pos == totalSize); return RPC_SUCCESS; } 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)) return err->err_code; return RPC_SUCCESS; }
整个工程的代码随后上传: