• 网络服务器发送封包设计


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

    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,后续会修正,整体结构基本不会改变,可能会加上组包和超时处理。

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



  • 相关阅读:
    MFC OnPaint()函数中最先调用CDialog::OnPaint()和最后调用CDialog::OnPaint()的巨大区别
    教你如何快速使用Github
    NET开发者部署React-Native
    分层架构
    微内核架构(Microkernel Architecture)
    ABP-N层架构
    MVC 应用免受 CSRF攻击
    Redis时延问题
    JS call与apply
    jQuery插件编写
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2429629.html
Copyright © 2020-2023  润新知