<iframe align="top" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog01.html" frameborder="0" width="728" scrolling="no" height="90"></iframe>
打包传输结构体或大内存块<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
作者 郑昀
内容 |
|
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 49.5pt; HEIGHT: 6pt" alt="" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5Czhengyun%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:href="mhtml:file://F:\CodesLife\IBMTech\痛苦的遗忘——给应用程序开发人员的几句忠告.mht!http://www-900.ibm.com/developerWorks/cn/i/c.gif"></imagedata></shape> |
BSTR的解法 |
<shape id="_x0000_i1026" style="WIDTH: 49.5pt; HEIGHT: 6pt" alt="" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5Czhengyun%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:href="mhtml:file://F:\CodesLife\IBMTech\痛苦的遗忘——给应用程序开发人员的几句忠告.mht!http://www-900.ibm.com/developerWorks/cn/i/c.gif"></imagedata></shape> |
SAFEARRAY的解法 |
<shape id="_x0000_i1027" style="WIDTH: 49.5pt; HEIGHT: 6pt" alt="" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5Czhengyun%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:href="mhtml:file://F:\CodesLife\IBMTech\痛苦的遗忘——给应用程序开发人员的几句忠告.mht!http://www-900.ibm.com/developerWorks/cn/i/c.gif"></imagedata></shape> |
boost::serialization的解法 |
<shape id="_x0000_i1028" style="WIDTH: 49.5pt; HEIGHT: 6pt" alt="" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5Czhengyun%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:href="mhtml:file://F:\CodesLife\IBMTech\痛苦的遗忘——给应用程序开发人员的几句忠告.mht!http://www-900.ibm.com/developerWorks/cn/i/c.gif"></imagedata></shape> |
IStream流的解法 |
<shape id="_x0000_i1029" style="WIDTH: 49.5pt; HEIGHT: 6pt" alt="" type="#_x0000_t75"><imagedata src="file:///C:%5CDOCUME~1%5Czhengyun%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png" o:href="mhtml:file://F:\CodesLife\IBMTech\痛苦的遗忘——给应用程序开发人员的几句忠告.mht!http://www-900.ibm.com/developerWorks/cn/i/c.gif"></imagedata></shape> |
本文假定您熟悉 SAFEARRAY、C++、BOOST 和 MSMQ。
摘要:本文阐述了结构体/大内存块分布式传输时常用的四种打包方法,并演示了您如何利用这四种方法通过MSMQ发送/读取数据。
有时候我们需要远程传输各种结构体或者数据块,比如您通过MSMQ消息队列传递任意大小的结构体或者接口指针,那么如何打包传递呢?这实际上可以分解为一个普适问题:
如何把一个结构体(Structure Object)或者巨大内存块(比如5MB左右)打包为二进制数据流或者PROPVARIANT-compatible的类型?
本文介绍了四种传输方法:
一个BSTR;
一个SAFEARRAY;
boost::serialization;
IStream流。
本文还介绍了如何从MSMQ收发/解析这四种类型的数据。
BSTR的解法
BSTR的解法应该是这里面最简单的,也最容易理解的解法。
从BSTR定义 “一个 BSTR 是预先确定长度的 OLECHAR(每个 16 位)缓冲区” 看,BSTR并不等同于OLECHAR,它的前面还提供了4个字节,用于保留字符串的长度。BSTR真正指向第五个字节,也就是真正的OLECHAR串的开始处。
由此我们通常可以使用 BSTR 前缀来判断OLECHAR是多少个字节,它们并不会把数据解释为字符串,因此数据可以是具有嵌入空值(0x00)的二进制数据。这个特性正好为我们所用。
另外一个需要注意的问题是,我们要调用 SysStringByteLen 来获得 BSTR 中字节的数量,而不是单纯地计算Unicode字符的数量。
首先,我们给出一个要传输的类定义,它拥有几个常见类型的成员变量:
class A
{
int i; unsigned int ui;
long l;unsigned long ul;
char szInt[MAX_PATH];
std::string strLong;
public:
A() :
i(std::rand()),
ui(std::rand()),
l(std::rand()),
ul(std::rand())
{
std::stringstream ss;
ss ss >> szInt;
ss.clear();
ss ss >> strLong;
}
};
|
打包很容易:
A aSend;
BSTR bstrSend = SysAllocStringByteLen(NULL, sizeof(aSend));
LPBYTE pv = reinterpret_cast<byte>(bstrSend);<p></p></byte>
CopyMemory(pv, (void *)&aSend, sizeof(aSend));
|
这里要解释一下MSMQ接收消息体的规则。智能指针IMSMQMessagePtr的Body属性接收_variant_t参数。所以如果我们想把类对象实例作为消息的Body写入MSMQ消息队列,我们需要事先转换为_variant_t,下面的代码就是做这种转换的:
IMSMQMessagePtr spMsg("MSMQ.MSMQMessage");
CComBSTR bstrBody;
bstrBody.AppendBSTR(pData);
CComVariant varBody (bstrBody);
spMsg->Body = varBody;
hr = spMsg->Send(spQueue);
|
就这样,消息发送到了MSMQ。
下面我们演示如何解包。
A aRead;
IMSMQMessagePtr pMsg;
ReadMSMQMessage(spQueueRead, pMsg);
UINT uiRead = SysStringByteLen(pMsg->Body.bstrVal);
LPBYTE pvRead = reinterpret_cast<byte>(pMsg->Body.bstrVal);<p></p></byte>
CopyMemory((void *)&aRead, pvRead, uiRead);
|
新的类对象实例aRead的数据经过这样的解包,就得到了aSend的数据。
SAFEARRAY的解法
SAFEARRAY的解法较BSTR解法复杂了一点,不过就本质而言,它也是简单地把结构体复制到字节数组中。SAFEARRAY是一个带有边界信息的数组,它只是数组的描述,并不是数组本身,真正的数组内容存储在一个单独的内存块中,SAFEARRAY中的pvData指向这个内存块。
值得注意的是,这种方式一次只能打包65536字节以下的数据,这是由于
SafeArrayCreateVector的cElements定义所限制的:
SAFEARRAY* SafeArrayCreateVector(
VARTYPE vt,
long lLbound,
unsigned int cElements);
|
我们通常会用SafeArrayCreateVector API创建一个单维SAFEARRAY,分配一个sizeof(DATA)大小的连续内存块,而这个函数的第三个参数是一个unsigned int类型,所以最大值就只能是65536了。
下面的代码演示如何打包类A,
A aSend;
_variant_t varBody;
|
使用SafeArrayCreateVector API创建一个单维SAFEARRAY:
LPSAFEARRAY lpsa = SafeArrayCreateVector(VT_UI1, 0, Size); |
在你访问SAFEARRAY数据之前,你必须调用SafeArrayAccessData,该函数锁定数据并且返回一个指针。在这里,锁定数组意味着增加该数组的内部计数器:
LPBYTE pbData = NULL;
if (lpsa)
hr = SafeArrayAccessData(lpsa, (void **)&pbData);
|
将类对象实例的内存复制到pbData,并将varBody和我们的单维SAFEARRAY拉上关系:
if (SUCCEEDED(hr))
{
CopyMemory(pbData, (void *) &aSend, sizeof(*pData));
varBody.vt = VT_ARRAY|VT_UI1;
varBody.parray = lpsa;
}
|
相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数:
if (pbData)
SafeArrayUnaccessData(varBody.parray);
|
填写MSMQMessage的Body属性:
IMSMQMessagePtr spMsg("MSMQ.MSMQMessage");
spMsg->Body = varBody;
|
好了,我们可以把这个消息体发送到MSMQ了。
收到MSMQ消息,反解也是依样画葫芦,
HRESULT ChangeVariant2Struct (_variant_t &var, A *DP)
{
SAFEARRAY* psa = var.parray;
|
调用SafeArrayGetUBound和SafeArrayGetLBound得到SAFEARRAY的上下边界:
long lBound;
SafeArrayGetLBound(psa, 1, &lBound);
long lUp;
SafeArrayGetUBound(psa, 1, &lUp);
DWORD dwSize = lUp - lBound + 1;
if(dwSize
return S_FALSE;
|
从而计算出要复制的内存块的大小。
下面开始复制:
void * tp;
SafeArrayAccessData(psa, reinterpret_castvoid**>(&tp));
CopyMemory((LPVOID)DP, tp, dwSize);
SafeArrayUnaccessData(psa);
return S_OK;
}
|
下面演示如何调用上面定义的函数ChangeVariant2Struct,从消息Body属性中得到类A的实例:
A aRead;
ZeroMemory((PVOID)&aRead, sizeof(aRead));
hr = ChangeVariant2Struct(pIMQMsg->Body,
&aRead);
|
boost::serialization的解法
boost.<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">1.32.0</chsdate>于<chsdate year="2004" month="11" day="19" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: Arial; mso-bidi-font-size: 12.0pt">2004</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-bidi-font-size: 12.0pt; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">年</span><span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: Arial; mso-bidi-font-size: 12.0pt">11</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-bidi-font-size: 12.0pt; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">月</span><span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: Arial; mso-bidi-font-size: 12.0pt">19</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Arial; mso-bidi-font-size: 12.0pt; mso-hansi-font-family: Arial; mso-bidi-font-family: Arial">日</span></chsdate>发布,其中Robert Ramey的boost::serialization库可以将C++数据结构的任意集可逆地解构为一系列字节流。字节流的承载形式可以表现为:一个二进制数据的文件、文本数据、XML等。boost::serialization库完全是平台独立的。
我们借用它的一个例子来讲述我们的故事。首先你的类定义需要扩充:
class A
{
friend class boost::serialization::access;
templateclass Archive>
void serialize(Archive & ar, const unsigned int /* version */){
ar & i & ui & l & ul & szBuf;
}
。。。
};
|
由于C++没有reflection能力,无法动态查询对象内部信息以及对象所属类的信息,所以不但要加入一个友元,还需要用户介入serialize方法的具体细节。
另外我们还要借用boost_1_32_0\libs\serialization\example中提供的两个头文件:portable_binary_iarchive.hpp和portable_binary_oarchive.hpp。
之后的打包就简洁多了:
std::stringstream ssSend;
std::string strSend;
{
portable_binary_oarchive pboa(ssSend);
pboa
strSend = ssSend.str();
}
|
ssSend里就承载着二进制数据流。除此之外,你还可以用
A aFile;
std::ofstream ofs(“filename.bin”);
boost::archive::text_oarchive oa(ofs);
oa
|
直接将数据流序列化到二进制数据文件中,这也可以作为传输的介质。
为了把strSend发送到MSMQ,我们还需要:
CComBSTR bstr;
bstr.AppendBytes(strSend.c_str(),strSend.length());
CComVariant var(bstr);
spMsg->Body = var;
|
收到的MSMQ消息解包也很简单:
A aRead;
std::stringstream ssRead;
ssRead
portable_binary_iarchive pbia(ssRead);
pbia >> aRead;
|
这种font-size: 10.
分享到:
相关推荐
归档文件可用于备份数据或将多个文件打包为单一文件以便于传输。 #### Support 扶持,支持 在软件开发中,“support”通常指为用户提供帮助和支持的服务。这可能包括技术支持、文档、培训等。 #### Dispatch 分派,...
4. **多线程处理**:为了实现并发处理,如同时接收多条消息或文件传输,飞鸽传书源码会使用C++的多线程功能。`std::thread`库提供了创建和管理线程的机制,而互斥锁(mutexes)、条件变量(condition variables)等...
在JavaScript中,可以通过监听`window.onload`事件或检查DOM元素的状态来判断页面是否完全加载。 ### 9. 十进制转二进制 题目要求将十进制数“100010”转换为二进制,并要求使用相同方法得到“11000”。这涉及到二...
标题中的"FileZilla_3.0.0-beta1_src.tar.gz"指的是FileZilla服务器版本3.0.0的beta1测试版的源代码,该源代码被打包成tar.gz格式的压缩文件。这个版本是用于开发者和爱好者进行研究、定制或改进FileZilla服务器软件...
在MFC中,同步通信通常通过阻塞调用来实现,如`ReadFile`和`WriteFile`函数,当数据未准备好或未完全发送时,这些函数会挂起执行,直到数据传输完成。 异步通信则允许程序在等待数据传输的同时执行其他任务,提高...
2. `src` 或 `java` / `cpp` / `python` 文件夹:根据protobuf生成的源代码,分别对应Java、C++或Python等语言的实现。 3. `client` 和 `server` 文件夹:分别包含客户端和服务端的实现代码,它们使用protobuf生成的...
1. **C++ API**:UE4 API主要是基于C++构建的,包括各种类、结构体、枚举、函数等,它们定义了游戏对象、组件、材质、动画、物理系统、输入管理等功能。C++ API允许开发者直接操作引擎内部的数据结构,实现高度...
C#(发音为“看-沙普”)是一种由Microsoft开发的简单易用的现代编程语言,它不仅是一种面向对象的语言,还是一种类型安全的语言。C#被设计为易于让C、C++和Java程序员快速上手。C#是由ECMA国际标准化为ecma-334标准...
- **应用场景**:C++中的指针操作、结构体成员访问。 **41. ASP (Active Server Page) 活动服务器页面** - **定义**:一种用于生成动态网页的技术。 - **应用场景**:Web应用开发、服务器端脚本编写。 **42. ASP...
RAR是一种常见的文件压缩格式,用于将多个文件打包成一个单一的文件,方便存储和传输。用户需要使用解压工具来打开并查看源代码。 【标签】 1. **毕业设计**:这可能意味着这个项目是作为计算机科学或相关专业学生...
1. **源代码文件**:包含C或C++语言编写的源代码,实现触摸屏数据处理的核心功能。 2. **头文件**:定义了对外的接口和结构体,供其他应用程序调用tslib的功能。 3. **配置文件**:如Makefile,用于编译和构建过程的...
**MPI**(Message Passing Interface)是一种广泛应用于并行计算的标准接口库,它提供了一系列函数和宏定义,支持C、FORTRAN以及C++等语言编写的程序通过消息传递来实现多处理器间的通信与协作。MPI最初由来自工业界、...