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 協議

結尾:

只爲記錄,只爲分享! 願所寫能對你有所幫助。不忘記點個贊,謝謝~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章