网络服务器发送封包设计

接上篇,本文介绍发送封包的设计.

WPacket的构成与RPacket类似,实际数据都存放在buffer组成的链表中.发送时,如果一个WPacket的数据跨越了两个buffer,可通过WSASend提交
多个缓冲,一次性将数据发出去.


WPacket还提供了一个传入RPacket的构造函数,以方便网关类程序收到一个包就马上将其转发的需求.一般情况下,从一个RPacket构造的WPacket都
不需要向其写入新的数据,而是直接转发.所以.构造的这个WPacket和传入的RPacket是共享buffer的.这样大大减少了内存拷贝的次数.当第一次向这
个WPacket写入数据时,将会触发拷贝过程,将RPacket中的数据拷贝到一个生成的buffer之中,至此,WPacket与构造传入的RPacket将不再共享buffer,
所有的数据将会被写入到WPacket自己的buffer中.

代码如下:

const static unsigned short sizeOfLen = sizeof(unsigned short);
const static unsigned short cmdlen = sizeof(unsigned short);

#define MAX_PACKET_SIZE 65535

class WPacket
{
friend class Connection;
public:
WPacket(const RPacket &rpk):m_copyOnWrite(true),m_writePos(0),m_head(rpk.m_head)
,m_buf(rpk.m_buf),m_writeBuf(rpk.m_buf),m_len(rpk.m_len)
{

}

WPacket():m_buf(0),m_writeBuf(0),m_head(0),m_writePos(sizeOfLen+cmdlen)
,m_len(sizeOfLen+cmdlen)
{

}

WPacket(const WPacket &wpk):m_buf(wpk.m_buf),m_writeBuf(wpk.m_writeBuf),
m_head(wpk.m_head),m_writePos(wpk.m_writePos),m_len(wpk.m_len),m_copyOnWrite(wpk.m_copyOnWrite)
{

}

WPacket & operator = (const WPacket &other)
{
if(this == &other)
return *this;
m_head = other.m_head;
m_len = other.m_len;
m_buf = other.m_buf;
m_writePos = other.m_writePos;
m_copyOnWrite = other.m_copyOnWrite;
return *this;
}

void WriteShort(unsigned short value)
{
Write<unsigned short>(value);
}

//.......其它Write

void WriteString(const char *str)
{
unsigned short strLen = strlen(str) + 1;
WriteBinary((const void *)str,strLen);
}

void WriteBinary(const void *bin,unsigned short len)
{
CopyOnWrite();
CreateBuffer();
//先写入长度
WriteShort(len);
unsigned short sizeRemain = m_writeBuf->m_bufSize - m_writePos;
if(sizeRemain >=len)
{
memcpy(&m_writeBuf->m_buf[m_writePos],bin,len);
m_writePos += len;
m_writeBuf->m_dataSize += len;
}
else
{
const char *ptr = (const char *)bin;
unsigned short copySize = sizeRemain;
memcpy(&m_writeBuf->m_buf[m_writePos],ptr,copySize);
ptr += copySize;
m_writeBuf->m_dataSize += copySize;
m_writePos = 0;
copySize = len - copySize;
rptr<buffer> newbuf = new buffer(copySize);
m_writeBuf->m_next = newbuf;
m_writeBuf = newbuf;
memcpy(&m_writeBuf->m_buf[m_writePos],ptr,copySize);
m_writePos += copySize;
m_writeBuf->m_dataSize += copySize;
}
*(unsigned short*)&m_writeBuf->m_buf[m_head] += (len);
m_len = *(unsigned short*)&m_writeBuf->m_buf[m_head];
}

private:

void CreateBuffer()
{
if(m_buf._nil())
{
m_buf = m_writeBuf = new buffer(4096);
}
}


void CopyOnWrite()
{

if(!m_copyOnWrite)
return;

unsigned short totalSize = m_len;
rptr<buffer> tmp = new buffer(totalSize > 0 ? totalSize : 4096);
if(totalSize > 0)
{
//复制数据
unsigned short copySize = totalSize;
rptr<buffer> cur = m_buf;
while(copySize > 0)
{
unsigned short curDataRemian = cur->m_bufSize - m_head;
if(curDataRemian >= copySize)
{
memcpy(&tmp->m_buf[m_writePos],&cur->m_buf[m_head],copySize);
m_writePos += copySize;
m_buf = m_writeBuf = tmp;
m_head = 0;
copySize = 0;
}
else
{
memcpy(&tmp->m_buf[m_writePos],&cur->m_buf[m_head],curDataRemian);
m_writePos += curDataRemian;
copySize -= curDataRemian;
cur = cur->m_next;
m_head = 0;
}
}
}
else
{
m_buf = m_writeBuf = tmp;
m_head = 0;
m_writePos = m_head + sizeOfLen + cmdlen;
m_len = sizeOfLen + cmdlen;
}
m_buf->m_dataSize = m_len;
m_copyOnWrite = false;

}

template <typename T>
void Write(const T &value)
{
CopyOnWrite();
CreateBuffer();
unsigned short sizeRemain = m_writeBuf->m_bufSize - m_writePos;
if(sizeRemain >= sizeof(T))
{
*(T*)&m_writeBuf->m_buf[m_writePos] = value;
m_writePos += sizeof(T);
m_writeBuf->m_dataSize += sizeof(T);
}
else
{

char *ptr = (char*)&value;
unsigned short copySize = sizeRemain;
memcpy(&m_writeBuf->m_buf[m_writePos],ptr,copySize);
ptr += copySize;
m_writeBuf->m_dataSize += copySize;
m_writePos = 0;
copySize = sizeof(T) - copySize;
rptr<buffer> newbuf = new buffer(copySize);
m_writeBuf->m_next = newbuf;
m_writeBuf = newbuf;
memcpy(&m_writeBuf->m_buf[m_writePos],ptr,copySize);
m_writePos += copySize;
m_writeBuf->m_dataSize += copySize;

}
*(T*)&m_writeBuf->m_buf[m_head] += sizeof(T);
m_len = *(unsigned short*)&m_writeBuf->m_buf[m_head];
}

private:
bool m_copyOnWrite;//是否需要写时拷贝
unsigned short m_len;
unsigned short m_writePos;
unsigned short m_head; //在buf中的起始下标
rptr<buffer> m_writeBuf;//当前writePos所在的buf
rptr<buffer> m_buf;//存放packet的数据,可能由一组m_buf构成链表
};

项目的完整代码在这里:

http://download.csdn.net/detail/sniperhuangwei/4083117

尚未经过完整测试,Packet位移上的处理可能还有bug,后续会修正,整体结构基本不会改变,可能会加上组包和超时处理。

也就是当套接口要发送的数据量太小时暂时不发送,等待后续的写请求或触发超时才将数据写出去。



原文地址:https://www.cnblogs.com/sniperHW/p/2429629.html