服務端使用c++實現websocket協議解析及通訊

來源:互聯網
上載者:User

標籤:介紹   str   bre   點擊   方法   csdn   while   tail   服務端   

轉自:http://blog.csdn.net/grafx/article/details/54234518

 

 

       WebSocket 設計出來的目的就是要使用戶端瀏覽器具備像 C/S 架構下案頭系統的即時通訊能力。 瀏覽器通過 JavaScript 向伺服器發出建立 WebSocket 串連的請求,串連建立以後,用戶端和伺服器端就可以通過 TCP 串連直接交換資料。因為 WebSocket 串連本質上就是一個 TCP 串連,所以在資料轉送的穩定性和資料轉送量的大小方面,和輪詢以及 Comet 技術比較,具有很大的效能優勢。下面是一個簡單 Web 應用程式分別用輪詢方式和 WebSocket 方式來實現,下面是測試結果圖:


                                                        通過這張圖可以清楚的看出,在流量和負載增大的情況下,WebSocket 方案相比傳統的 Ajax 輪詢方案有極大的效能優勢。       好了不過多介紹 WebSocket 了,更多介紹大家可以點擊參考資料引用的連結查看,還是回到解析協議及通訊上來。解析協議這種事,就得耐著性子,一個位元組一個位元組解析,按步驟一點一點寫程式。不過讀懂了文檔,知道了每個位元組的屬性意義後,解析起來還是挺簡單的。按照協議說明,一旦完成資料解碼,那麼編碼就稍微容易一些,差不多就是解碼的逆向操作了。服務端使用c++完成 WebSocket 通訊,主要需要完成以下三方面編程:       1. 服務端與h5用戶端發起的 WebSocket 串連握手:int wsHandshake(string &request, string &response){    // 解析http要求標頭資訊    int ret = WS_STATUS_UNCONNECT;    std::istringstream stream(request.c_str());    std::string reqType;    std::getline(stream, reqType);    if (reqType.substr(0, 4) != "GET ")    {        return ret;    }    std::string header;    std::string::size_type pos = 0;    std::string websocketKey;    while (std::getline(stream, header) && header != "\r")    {        header.erase(header.end() - 1);        pos = header.find(": ", 0);        if (pos != std::string::npos)        {            std::string key = header.substr(0, pos);            std::string value = header.substr(pos + 2);            if (key == "Sec-WebSocket-Key")            {                ret = WS_STATUS_CONNECT;                websocketKey = value;                break;            }        }    }    if (ret != WS_STATUS_CONNECT)    {        return ret;    }    // 填充http回應標頭資訊    response = "HTTP/1.1 101 Switching Protocols\r\n";    response += "Upgrade: websocket\r\n";    response += "Connection: upgrade\r\n";    response += "Sec-WebSocket-Accept: ";    const std::string magicKey("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");    std::string serverKey = websocketKey + magicKey;    char shaHash[32];    memset(shaHash, 0, sizeof(shaHash));    sha1::calc(serverKey.c_str(), serverKey.size(), (unsigned char *) shaHash);    serverKey = base64::base64_encode(std::string(shaHash)) + "\r\n\r\n";    string strtmp(serverKey.c_str());    response += strtmp;    return ret;}       2. 完成握手後串連就建立了。然後就是接收h5用戶端通過 WebSocket 發過來的資料幀並解碼:int wsDecodeFrame(string inFrame, string &outMessage){    int ret = WS_OPENING_FRAME;    const char *frameData = inFrame.c_str();    const int frameLength = inFrame.size();    if (frameLength < 2)    {        ret = WS_ERROR_FRAME;    }    // 檢查擴充位並忽略    if ((frameData[0] & 0x70) != 0x0)    {        ret = WS_ERROR_FRAME;    }    // fin位: 為1表示已接收完整報文, 為0表示繼續監聽後續報文    ret = (frameData[0] & 0x80);    if ((frameData[0] & 0x80) != 0x80)    {        ret = WS_ERROR_FRAME;    }    // mask位, 為1表示資料被加密    if ((frameData[1] & 0x80) != 0x80)    {        ret = WS_ERROR_FRAME;    }    // 作業碼    uint16_t payloadLength = 0;    uint8_t payloadFieldExtraBytes = 0;    uint8_t opcode = static_cast<uint8_t >(frameData[0] & 0x0f);    if (opcode == WS_TEXT_FRAME)    {        // 處理utf-8編碼的文本幀        payloadLength = static_cast<uint16_t >(frameData[1] & 0x7f);        if (payloadLength == 0x7e)        {            uint16_t payloadLength16b = 0;            payloadFieldExtraBytes = 2;            memcpy(&payloadLength16b, &frameData[2], payloadFieldExtraBytes);            payloadLength = ntohs(payloadLength16b);        }        else if (payloadLength == 0x7f)        {            // 資料過長,暫不支援            ret = WS_ERROR_FRAME;        }    }    else if (opcode == WS_BINARY_FRAME || opcode == WS_PING_FRAME || opcode == WS_PONG_FRAME)    {        // 二進位/ping/pong幀暫不處理    }    else if (opcode == WS_CLOSING_FRAME)    {        ret = WS_CLOSING_FRAME;    }    else    {        ret = WS_ERROR_FRAME;    }    // 資料解碼    if ((ret != WS_ERROR_FRAME) && (payloadLength > 0))    {        // header: 2位元組, masking key: 4位元組        const char *maskingKey = &frameData[2 + payloadFieldExtraBytes];        char *payloadData = new char[payloadLength + 1];        memset(payloadData, 0, payloadLength + 1);        memcpy(payloadData, &frameData[2 + payloadFieldExtraBytes + 4], payloadLength);        for (int i = 0; i < payloadLength; i++)        {            payloadData[i] = payloadData[i] ^ maskingKey[i % 4];        }        outMessage = payloadData;        delete[] payloadData;    }    return ret;}       3. 解碼完資料幀,服務端做出相應處理後將結果按照 WebSocket 通訊協定編碼,然後發給h5用戶端:int wsEncodeFrame(string inMessage, string &outFrame, enum WS_FrameType frameType){    int ret = WS_EMPTY_FRAME;    const uint32_t messageLength = inMessage.size();    if (messageLength > 32767)    {        // 暫不支援這麼長的資料        return WS_ERROR_FRAME;    }    uint8_t payloadFieldExtraBytes = (messageLength <= 0x7d) ? 0 : 2;    // header: 2位元組, mask位設定為0(不加密), 則後面的masking key無須填寫, 省略4位元組    uint8_t frameHeaderSize = 2 + payloadFieldExtraBytes;    uint8_t *frameHeader = new uint8_t[frameHeaderSize];    memset(frameHeader, 0, frameHeaderSize);    // fin位為1, 擴充位為0, 操作位為frameType    frameHeader[0] = static_cast<uint8_t>(0x80 | frameType);    // 填充資料長度    if (messageLength <= 0x7d)    {        frameHeader[1] = static_cast<uint8_t>(messageLength);    }    else    {        frameHeader[1] = 0x7e;        uint16_t len = htons(messageLength);        memcpy(&frameHeader[2], &len, payloadFieldExtraBytes);    }    // 填充資料    uint32_t frameSize = frameHeaderSize + messageLength;    char *frame = new char[frameSize + 1];    memcpy(frame, frameHeader, frameHeaderSize);    memcpy(frame + frameHeaderSize, inMessage.c_str(), messageLength);    frame[frameSize] = ‘\0‘;    outFrame = frame;    delete[] frame;    delete[] frameHeader;    return ret;}       4. 握手只需一次,隨後反覆執行第2步及第3步,就完成了服務端與h5用戶端通訊。這個只是c++版本的,可以很容易改成java版本的。下面是上述方法用到的一些枚舉:enum WS_Status{    WS_STATUS_CONNECT = 0,    WS_STATUS_UNCONNECT = 1,};enum WS_FrameType{    WS_EMPTY_FRAME = 0xF0,    WS_ERROR_FRAME = 0xF1,    WS_TEXT_FRAME   = 0x01,    WS_BINARY_FRAME = 0x02,    WS_PING_FRAME = 0x09,    WS_PONG_FRAME = 0x0A,    WS_OPENING_FRAME = 0xF3,    WS_CLOSING_FRAME = 0x08};       參考資料:       https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_server       https://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/

 

(轉)服務端使用c++實現websocket協議解析及通訊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.