C++实现WebSocket解析协议

前提:

本人最近做的项目,服务器端用的是C++写的,而与客户端交互用的是websocket,服务器端要想正常的使用数据,必须要对websocket协议进行解析。

解析握手协议见我上一章内容: C++实现websocket服务器握手协议

WebSocket数据格式

  1. FIN:表示这个数据是不是接收完毕,为1表示收到的数据是完整的,占1bit
  2. RSV1~3:用于扩展,通常都为0,各占1bit
  3. OPCODE:表示报文的类型,占4bit  
    • 0x00:标识一个中间数据包
    • 0x01:标识一个text数据包
    • 0x02:标识一个二进制数据包
    • 0x03~07:保留
    • 0x08:标识一个断开连接数据包
    • 0x09:标识一个ping数据包
    • 0x0A:标识一个pong数据包
    • 0x0B~F:保留
  4. MASK:用于表示数据是否经常掩码处理,为1时,Masking-key即存在,占1bit
  5. Payload len:表示数据长度,即Payload Data的长度,当Payload len为0~125时,表示的值就是Payload Data的真实长度;当Payload len为126时,报文其后的2个字节形成的16bits无符号整型数的值是Payload Data的真实长度(网络字节序,需转换);当Payload len为127时,报文其后的8个字节形成的64bits无符号整型数的值是Payload Data的真实长度(网络字节序,需转换);
  6. Masking-key:掩码,当Mask为1时存在,占4字节32bit
  7. Payload Data:表示数据

C++对websocket协议处理

/**
 * @brief getWSFrameData    解析websocket的协议包,不能解决粘包半包问题
 * @param msg               待解析的数据
 * @param msgLen            待解析的数据长度
 * @param outBuf            解析完成数据
 * @return
 */
int unPackingWSFrameData(char *msg,
                         int msgLen,
                         std::vector<char> &outBuf)
{
    //报文长度一定大于2字节,对于小于的,做返回处理
    if(msgLen < 2)
    {
        return -3;
    }

    uint8_t opcode_ = 0;
    uint8_t mask_ = 0;
    uint8_t masking_key_[4] = {0,0,0,0};
    uint64_t payload_length_ = 0;
    int pos = 0;

    //Opcode
    opcode_ = msg[pos] & 0x0f;
    pos++;
    //MASK
    mask_ = (unsigned char)msg[pos] >> 7;
    //Payload length
    payload_length_ = msg[pos] & 0x7f;
    pos++;
    if(payload_length_ == 126)
    {
        uint16_t length = 0;
        memcpy(&length, msg + pos, 2);
        pos += 2;
        payload_length_ = ntohs(length);
    }
    else if(payload_length_ == 127)
    {
        uint32_t length = 0;
        memcpy(&length, msg + pos, 8);
        pos += 8;
        payload_length_ = ntohl(length);
    }
    //Masking-key
    if(mask_ == 1)
    {
        for(int i = 0; i < 4; i++)
        {
            masking_key_[i] = msg[pos + i];
        }
        pos += 4;
    }
    //取出消息数据
    if (msgLen >= pos + payload_length_ )
    {
        outBuf.clear();
        if(mask_ != 1)
        {
            char* dataBegin = msg + pos;
            outBuf.insert(outBuf.begin(), dataBegin, dataBegin+payload_length_);
        }
        else
        {
            for(uint i = 0; i < payload_length_; i++)
            {
                int j = i % 4;
                outBuf.push_back(msg[pos + i] ^ masking_key_[j]);
            }
        }

    }
    else
    {
        //此时包长小于报文中记录的包长
        return -2;
    }

    //断开连接类型数据包
    if ((int)opcode_ == 0x8)
        return -1;

    return 0;
}

以上函数即实现了对收到websocket数据的解析,返回结果为:vector<char>output;

通常会在函数外面对此进行转换为char*,方便我们使用,见下:

vector<char>output;
char* out = &output[0];

当然,现在的解析还不是完美的解决方法,因为在实际的使用当中,会存在接收的包粘包,半包等等问题,而以上函数只能解决收到包正好是一个完整的包的情况;具体解决粘包半包问题,留待下次博客吧!

参考:

C++ 使用 websocket 协议

结尾:

只为记录,只为分享! 愿所写能对你有所帮助。不忘记点个赞,谢谢~

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值