1、以 windows IOCP 为例,complete 线程在接收到数据后,应该马上将接收到的数据拷贝到解包缓存,然后立即发起一次新的 WSARecv 操作。然后再对解包缓存执行操作,解析出逻辑包。通常的方法是将数据拷贝到一个环形缓冲中,以减少数据的拷贝次数。但在解出一个数据包之后,免不了还要将一个完整的数据包拷贝到另一个包缓存中,再将这个包提交给应用层处理.本文介绍一种,拼包方式,以避免从解包缓存 copy 数据到逻辑包中.首先介绍逻辑包的组织结构:/RPacket 数据可跨越多个 bufferclass RPacketfriend class Connection;friend class
2、WPacket;public:RPacket(const RPacket bool _nil()return m_buf._nil();RPacketm_head = other.m_head;m_len = other.m_len;m_buf = other.m_buf;m_readPos = other.m_readPos;if(m_binBuffer)delete m_binBuffer;m_binBufferPos = 0;return *this;char ReadChar()return Read();short ReadShort()return Read();long Read
3、Long()return Read();float ReadFloat()return Read();double ReadDouble()return Read();short ReadCmd()return *(short*)const char* ReadString()unsigned int strLen = (unsigned int)Read();if(strLen = 0 | m_dataRemain m_bufSize - m_readPos;/当前 buf 还有多少有效数据char *str;if(sizeRemain = strLen) str = m_readPos +
4、= strLen;elseif(!m_binBuffer)m_binBuffer = new charm_len;m_binBufferPos = 0;str = unsigned int copySize = sizeRemain;memcpy(m_readBuf = m_readBuf-m_next;m_readPos = 0;m_binBufferPos += copySize;copySize = strLen - copySize;memcpy(m_readPos += copySize;m_dataRemain -= strLen;Arrange();return str;cons
5、t void* ReadBinary(unsigned short if(len = 0 | m_dataRemain m_bufSize - m_readPos;/当前 buf 还有多少有效数据if(sizeRemain = len)bin = m_readPos += len;elseif(!m_binBuffer)m_binBuffer = new charm_len;m_binBufferPos = 0;bin = unsigned int copySize = sizeRemain;memcpy(m_readBuf = m_readBuf-m_next;m_readPos = 0;m
6、_binBufferPos += copySize;copySize = len - copySize;memcpy(m_readPos += copySize;m_dataRemain -= len;Arrange();return bin;private:template T Read()if(m_readBuf._nil()return 0;unsigned int TypeSize = sizeof(T);if(m_dataRemain = TypeSize)ret = *(T*)m_readPos += TypeSize;else/数据跨越了两个数据块char tmpsizeof(T
7、);char *ptr = tmp;unsigned int copySize = sizeRemain;memcpy(ptr += copySize;copySize = TypeSize - copySize;m_readBuf = m_readBuf-m_next;m_readPos = 0;memcpy(m_readPos += copySize;ret = *(T*)m_dataRemain -= TypeSize;Arrange();return ret;RPacket(rptr void Arrange()if(m_readPos = m_readBuf-m_bufSize m_
8、readBuf = m_readBuf-m_next;private:unsigned short m_readPos;unsigned short m_head; /在 buf 中的起始下标unsigned short m_len; /packet 的总长度unsigned short m_dataRemain;/用于处理 ReadString,和 ReadBin 时数据跨越 buffer 的情况char * m_binBuffer;unsigned short m_binBufferPos;/rptr m_readBuf;/当前 readPos 所在的 bufrptr m_buf;/存放
9、packet 的数据,可能由一组 m_buf 构成链表;复制代码如代码所示,数据存放在由 m_buf 组成的 list 中,m_head 表明属于本RPacket 的数据在 m_buf 中的起始位置.一个 buf 块的大小为 65535 字节。如果包的长度不大,则 N 个 RPacket 可以共享同一个 buf,如果 RPacket 的数据大于 65535,则一个 RPacket 的数据则可能会使用超过一块 buf。由同一个套接口收到的所有 RPacket,其实际数据被一个 buf 链表链接着。buf 由基于引用计数的指针指向,当引用同一个 buf 块的 RPacket 都被释放之后,buf
10、也将会被释放。下面介绍数据接收和解包处理过程:bool Connection:Recv()if(m_tail._nil()m_CurRecvBuf = m_head = m_tail = new buffer(MAX_PACKET_SIZE);m_pos = m_totalDataSize = m_writePos = 0;unsigned short bufCount = 0;unsigned short RecvSize = MAX_PACKET_SIZE;unsigned short freeBufSize = m_CurRecvBuf-m_bufSize - m_writePos;m_
11、WRecvBufbufCount.buf = m_WRecvBufbufCount.len = freeBufSize;RecvSize -= freeBufSize;if(RecvSize 0)+bufCount;/不够 MAX_PACKET_SIZErptr tmp = new buffer(MAX_PACKET_SIZE);m_tail-m_next = tmp;m_tail = tmp;m_WRecvBufbufCount.buf = m_WRecvBufbufCount.len = RecvSize;return ConnectionBase:Recv(m_WRecvBuf,bufC
12、ount,(OVERLAPPED*)void Connection:OnRecvComplete(unsigned short dwBytesTransfered)unsigned short freeBufSize = m_CurRecvBuf-m_bufSize - m_writePos;if(freeBufSize = dwBytesTransfered)m_writePos += dwBytesTransfered;freeBufSize -= dwBytesTransfered;m_CurRecvBuf-m_dataSize += dwBytesTransfered;if(freeB
13、ufSize tmp = new buffer(MAX_PACKET_SIZE);m_tail-m_next = tmp;m_tail = tmp;m_CurRecvBuf = m_tail;m_writePos = 0;elsem_CurRecvBuf-m_dataSize = m_CurRecvBuf-m_bufSize;/开辟新空间rptr tmp = new buffer(MAX_PACKET_SIZE);m_tail-m_next = tmp;m_tail = tmp;m_CurRecvBuf = m_tail;m_writePos += (dwBytesTransfered - f
14、reeBufSize);m_CurRecvBuf-m_dataSize += (dwBytesTransfered - freeBufSize);m_totalDataSize += dwBytesTransfered;/Recv();RPacket Connection:UnPack()if(!m_head._nil()return RPacket(rptr(0),0);unsigned short packetLen = ReadPacketLen();if(packetLen = 0)return RPacket(rptr(0),0);if(m_totalDataSize (0),0);
15、/没有足够的数据/OK,数据充足,返回封包RPacket rpk(m_head,(unsigned short)m_pos);m_totalDataSize -= packetLen;/调整 m_head 和 m_posm_pos += packetLen;while(m_pos = MAX_PACKET_SIZE)rptr tmp = m_head;m_head = m_head-m_next;if(m_head._nil()m_tail = 0;m_pos = 0;elsem_pos -= MAX_PACKET_SIZE;return rpk;复制代码完成例程在 OnRecvComplet
16、e 中,把接收到的数据拷贝到 buf 链表中,马上启动新的 Recv 操作.后续的解包过程是 UnPack 函数,其作用就是将 buf 链表中的数据解包,并将RPacket 返回以供应用层处理,从代码中可以看出,解包过程是没有数据拷贝的,只需要正确设置RPacket 中的字段就可以了.基于 QT C+的网络缓冲区设计(一) 概括 缓冲区的关键是: 字符串数据 和 类型数据 之间的相互转换。比如将 char *的数据的按 1byte 1 byte 的读入转化为bool、char 、uint8 、int16、uint16 、int32(相当于 int),uint32、int64(相当于 long
17、long);当然还有 double。网络缓冲区分为发送端 和 接收端。发送端将类型数据 转换为 字符串数据。 接收端将字符数据 转换为 类型数据。一、发送端#ifndef SMPEnplexer_H #define SMPEnplexer_H#include “SMP_COMMAND.h“class SMPEnplexerpublic:SMPEnplexer(unsigned bufsize=8192); /default is 8KBSMPEnplexer();char* data() return _data;unsigned length() return _pos;void setLe
18、ngth(int pos)_pos += pos;void setBufferSize(int size);unsigned buffersize()return _bufSize;void reset();void addHead();/ add a argumentvoid addBoolArg(bool value);void addCharArg(char value);void addUInt8Arg(unsigned char value);void addInt16Arg(short value);void addUInt16Arg(short value) addInt16Ar
19、g(value); ;void addInt32Arg(int value);void addUInt32Arg(unsigned value) addInt32Arg(value); ;void addInt64Arg(long long int value);void addUInt64Arg(long long unsigned value) addInt64Arg(value); ;void addFloatArg(float value);void addStringArg(const QString void addCharArray(char* str,int len);PACK
20、AGE_HEAD head;protected:unsigned _bufSize;char *_data;unsigned _pos;QTextCodec* codec;#endif / SMPEnplexer_H二、接收端接收端将字符串数据流 转换为 相应的类型数据。#ifndef SMPDeplexer_H #define SMPDeplexer_H#include #include “SMP_COMMAND.h“class SMPDeplexerpublic:SMPDeplexer(unsigned bufSize = 4096);SMPDeplexer();char *data()
21、return _data;unsigned length() return _length;void setLength(int len) _length = len;unsigned buffersize() return _bufSize;void setBufferSize(unsigned size);void reset();/ demultiplex the next arg in the streambool boolArg();char charArg();unsigned char uint8Arg();short int16Arg();unsigned short uint
22、16Arg() return int16Arg(); int int32Arg();unsigned uint32Arg() return int32Arg(); long long int int64Arg();long long unsigned uint64Arg() return int64Arg(); void deplexHead();void deplexCharArray(char* str,int len);char *_data;unsigned _pos;unsigned _bufSize;unsigned _length;QTextCodec* codec;PACKAG
23、E_HEAD head;#endif / SMPDeplexer_H多字节与 UTF-8、Unicode 之间的转换 1.2.3. / 多字节编码转为 UTF8 编码 4. bool MBToUTF8(vector 8. 9. WCHAR* lpszW = NULL; 10. try 11. 12. lpszW = new WCHARnLen; 13. 14. catch(bad_alloc 17. 18. 19. int32 nRtn = MultiByteToWideChar(CP_ACP, 0, pmb, mLen, lpszW, nLen); 20. 21. if(nRtn != nL
24、en) 22. 23. delete lpszW; 24. return false; 25. 26. / convert an widechar string to utf8 27. int32 utf8Len = WideCharToMultiByte(CP_UTF8, 0, lpszW, nLen, NULL, 0, NULL, NULL); 28. if (utf8Len 49. 50. WCHAR* lpszW = NULL; 51. try 52. 53. lpszW = new WCHARnLen; 54. 55. catch(bad_alloc 58. 59. 60. int3
25、2 nRtn = MultiByteToWideChar(CP_UTF8, 0, pu8, utf8Len, lpszW, nLen); 61. 62. if(nRtn != nLen) 63. 64. delete lpszW; 65. return false; 66. 67. 68. / convert an widechar string to Multibyte 69. int32 MBLen = WideCharToMultiByte(CP_ACP, 0, lpszW, nLen, NULL, 0, NULL, NULL); 70. if (MBLen 91. 92. if (uL
26、en 113. if (MBLen 133. if (nLen 154. if (utf8Len();short ReadShort()return Read();long ReadLong()return Read();float ReadFloat()return Read();double ReadDouble()return Read();short ReadCmd()return *(short*)const char* ReadString()unsigned int strLen = (unsigned int)Read();if(strLen = 0 | m_dataRemai
27、n m_bufSize - m_readPos;/当前 buf 还有多少有效数据char *str;if(sizeRemain = strLen) str = m_readPos += strLen;elseif(!m_binBuffer)m_binBuffer = new charm_len;m_binBufferPos = 0;str = unsigned int copySize = sizeRemain;memcpy(m_readBuf = m_readBuf-m_next;m_readPos = 0;m_binBufferPos += copySize;copySize = strL
28、en - copySize;memcpy(m_readPos += copySize;m_dataRemain -= strLen;Arrange();return str;const void* ReadBinary(unsigned short if(len = 0 | m_dataRemain m_bufSize - m_readPos;/当前 buf 还有多少有效数据if(sizeRemain = len)bin = m_readPos += len;elseif(!m_binBuffer)m_binBuffer = new charm_len;m_binBufferPos = 0;b
29、in = unsigned int copySize = sizeRemain;memcpy(m_readBuf = m_readBuf-m_next;m_readPos = 0;m_binBufferPos += copySize;copySize = len - copySize;memcpy(m_readPos += copySize;m_dataRemain -= len;Arrange();return bin;private:template T Read()if(m_readBuf._nil()return 0;m_next;m_readPos = 0;memcpy(m_read
30、Pos += copySize;ret = *(T*)m_dataRemain -= TypeSize;Arrange();return ret;RPacket(rptr void Arrange()if(m_readPos = m_readBuf-m_bufSize m_readBuf = m_readBuf-m_next;private:unsigned short m_readPos;unsigned short m_head; /在 buf 中的起始下标unsigned short m_len; /packet 的总长度unsigned short m_dataRemain;/用于处理
31、 ReadString,和 ReadBin 时数据跨越 buffer 的情况char * m_binBuffer;unsigned short m_binBufferPos;/rptr m_readBuf;/当前 readPos 所在的 bufrptr m_buf;/存放 packet 的数据,可能由一组 m_buf 构成链表;如代码所示,数据存放在由 m_buf 组成的 list 中,m_head 表明属于本RPacket 的数据在 m_buf 中的起始位置.一个 buf 块的大小为 65535 字节。如果包的长度不大,则 N 个 RPacket 可以共享同一个 buf,如果 RPacket
32、 的数据大于 65535,则一个 RPacket 的数据则可能会使用超过一块 buf。由同一个套接口收到的所有 RPacket,其实际数据被一个 buf 链表链接着。buf 由基于引用计数的指针指向,当引用同一个 buf 块的 RPacket 都被释放之后,buf 也将会被释放。下面介绍数据接收和解包处理过程:bool Connection:Recv()if(m_tail._nil()m_CurRecvBuf = m_head = m_tail = new buffer(MAX_PACKET_SIZE);m_pos = m_totalDataSize = m_writePos = 0;unsi
33、gned short bufCount = 0;unsigned short RecvSize = MAX_PACKET_SIZE;unsigned short freeBufSize = m_CurRecvBuf-m_bufSize - m_writePos;m_WRecvBufbufCount.buf = m_WRecvBufbufCount.len = freeBufSize;RecvSize -= freeBufSize;if(RecvSize 0)+bufCount;/不够 MAX_PACKET_SIZErptr tmp = new buffer(MAX_PACKET_SIZE);m
34、_tail-m_next = tmp;m_tail = tmp;m_WRecvBufbufCount.buf = m_WRecvBufbufCount.len = RecvSize;return ConnectionBase:Recv(m_WRecvBuf,bufCount,(OVERLAPPED*)void Connection:OnRecvComplete(unsigned short dwBytesTransfered)unsigned short freeBufSize = m_CurRecvBuf-m_bufSize - m_writePos;if(freeBufSize = dwB
35、ytesTransfered)m_writePos += dwBytesTransfered;freeBufSize -= dwBytesTransfered;m_CurRecvBuf-m_dataSize += dwBytesTransfered;if(freeBufSize tmp = new buffer(MAX_PACKET_SIZE);m_tail-m_next = tmp;m_tail = tmp;m_CurRecvBuf = m_tail;m_writePos = 0;elsem_CurRecvBuf-m_dataSize = m_CurRecvBuf-m_bufSize;/开辟
36、新空间rptr tmp = new buffer(MAX_PACKET_SIZE);m_tail-m_next = tmp;m_tail = tmp;m_CurRecvBuf = m_tail;m_writePos += (dwBytesTransfered - freeBufSize);m_CurRecvBuf-m_dataSize += (dwBytesTransfered - freeBufSize);m_totalDataSize += dwBytesTransfered;/Recv();RPacket Connection:UnPack()if(!m_head._nil()retur
37、n RPacket(rptr(0),0);unsigned short packetLen = ReadPacketLen();if(packetLen = 0)return RPacket(rptr(0),0);if(m_totalDataSize (0),0);/没有足够的数据/OK,数据充足,返回封包RPacket rpk(m_head,(unsigned short)m_pos);m_totalDataSize -= packetLen;/调整 m_head 和 m_posm_pos += packetLen;while(m_pos = MAX_PACKET_SIZE)rptr tmp = m_head;m_head = m_head-m_next;if(m_head._nil()