• C++实现websocket协议通讯


      在获取服务器的数据时,我们传统的做法是通过前端进行请求服务器返回数据。这样如果我们要获取的数据不是连续的,或者服务器想想前端推送数据只能通过ajax等轮询请求了。html5以后我们可以通过websocket和服务器进行通信,前端和服务连接后就可以进行双工连接了。服务器有数据就能实时的给前端推送,而不需要我定时的去请求。

      websocket简单来说就是先前端向服务器发送http的请求,服务端通过前端请求的http的key值进过sha1和base64加密返回给前端,这一过程称为“握手”,如果验证成功就能进行websocket通讯了。握手成功后前端和后端通讯都要符合webscoket的协议才能通讯。关于websocket的协议大家可以网上查找,这里不作讨论。

      最近在要实现一个后端将设配读到数据主动发前端的功能,网上查找了一些大佬写好程序进行参考更改。这里谢谢他们,希望没有侵权。下面是主要代码。

      

     1 #ifndef __WebSocketProtocol_H__
     2 #define __WebSocketProtocol_H__
     3 
     4 #include <string>
     5 
     6 using std::string;
     7 
     8 class CWebSocketProtocol
     9 {
    10 public:
    11     enum WS_Status
    12     {
    13         WS_STATUS_CONNECT = 0,
    14         WS_STATUS_UNCONNECT = 1,
    15     };
    16 
    17     enum WS_FrameType
    18     {
    19         WS_EMPTY_FRAME = 0xF0,
    20         WS_ERROR_FRAME = 0xF1,
    21         WS_TEXT_FRAME = 0x01,
    22         WS_BINARY_FRAME = 0x02,
    23         WS_PING_FRAME = 0x09,
    24         WS_PONG_FRAME = 0x0A,
    25         WS_OPENING_FRAME = 0xF3,
    26         WS_CLOSING_FRAME = 0x08
    27     };
    28 
    29     static CWebSocketProtocol * getInstance();
    30 
    31     int getResponseHttp(string &request, string &response);
    32     int wsDecodeFrame(string inFrame, string &outMessage);    //解码帧
    33     int wsEncodeFrame(string inMessage, string &outFrame, enum WS_FrameType frameType);    //编码帧打包
    34 
    35 private:
    36     CWebSocketProtocol();
    37     ~CWebSocketProtocol();
    38 
    39     class CGrabo
    40     {
    41     public:
    42         ~CGrabo()
    43         {
    44             if (m_inst != 0)
    45             {
    46                 delete m_inst;
    47                 m_inst = 0;
    48             }
    49         }
    50     };
    51 
    52     static CGrabo m_grabo;
    53     static    CWebSocketProtocol * m_inst;
    54 };
    55 
    56 #endif
      1 #include "WebSocketProtocol.h"
      2 #include <iostream>
      3 #include <sstream>
      4 #include <string.h>
      5 #include <arpa/inet.h>
      6 #include "sha1.h"
      7 #include "base64.h"
      8 
      9 const char * MAGIC_KEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
     10 
     11 CWebSocketProtocol::CGrabo CWebSocketProtocol::m_grabo;
     12 CWebSocketProtocol * CWebSocketProtocol::m_inst = 0;
     13 
     14 CWebSocketProtocol::CWebSocketProtocol()
     15 {
     16 }
     17 
     18 
     19 CWebSocketProtocol::~CWebSocketProtocol()
     20 {
     21 }
     22 
     23 
     24 
     25 CWebSocketProtocol * CWebSocketProtocol::getInstance()
     26 {
     27     if (m_inst == 0)
     28     {
     29         m_inst = new CWebSocketProtocol;
     30     }
     31 
     32     return m_inst;
     33 }
     34 
     35 int CWebSocketProtocol::getResponseHttp(string &request, string &response)
     36 {
     37     // 解析http请求头信息  
     38     int ret = WS_STATUS_UNCONNECT;
     39     std::istringstream stream(request.c_str());
     40     std::string reqType;
     41     std::getline(stream, reqType);
     42     if (reqType.substr(0, 4) != "GET ")
     43     {
     44         return ret;
     45     }
     46 
     47     std::string header;
     48     std::string::size_type pos = 0;
     49     std::string websocketKey;
     50     while (std::getline(stream, header) && header != "
    ")
     51     {
     52         header.erase(header.end() - 1);
     53         pos = header.find(": ", 0);
     54         if (pos != std::string::npos)
     55         {
     56             std::string key = header.substr(0, pos);
     57             std::string value = header.substr(pos + 2);
     58             if (key == "Sec-WebSocket-Key")
     59             {
     60                 ret = WS_STATUS_CONNECT;
     61                 websocketKey = value;
     62                 break;
     63             }
     64         }
     65     }
     66 
     67     if (ret != WS_STATUS_CONNECT)
     68     {
     69         return ret;
     70     }
     71 
     72     // 填充http响应头信息  
     73     response = "HTTP/1.1 101 Switching Protocols
    ";
     74     response += "Connection: upgrade
    ";
     75     response += "Sec-WebSocket-Accept: ";
     76 
     77     std::string serverKey = websocketKey + MAGIC_KEY;
     78 
     79     SHA1 sha;
     80     unsigned int message_digest[5];
     81     sha.Reset();
     82     sha << serverKey.c_str();
     83 
     84     sha.Result(message_digest);
     85     for (int i = 0; i < 5; i++) {
     86         message_digest[i] = htonl(message_digest[i]);
     87     }
     88     serverKey = base64_encode(reinterpret_cast<const unsigned char*>(message_digest), 20);
     89     response += serverKey;
     90     response += "
    ";
     91     response += "Upgrade: websocket
    
    ";
     92 
     93     return ret;
     94 }
     95 
     96 int CWebSocketProtocol::wsDecodeFrame(string inFrame, string &outMessage)
     97 {
     98     int ret = WS_OPENING_FRAME;
     99     const char *frameData = inFrame.c_str();
    100     const int frameLength = inFrame.size();
    101     if (frameLength < 2)
    102     {
    103         ret = WS_ERROR_FRAME;
    104     }
    105 
    106     // 检查扩展位并忽略  
    107     if ((frameData[0] & 0x70) != 0x0)
    108     {
    109         ret = WS_ERROR_FRAME;
    110     }
    111 
    112     // fin位: 为1表示已接收完整报文, 为0表示继续监听后续报文  
    113     ret = (frameData[0] & 0x80);
    114     if ((frameData[0] & 0x80) != 0x80)
    115     {
    116         ret = WS_ERROR_FRAME;
    117     }
    118 
    119     // mask位, 为1表示数据被加密  
    120     if ((frameData[1] & 0x80) != 0x80)
    121     {
    122         ret = WS_ERROR_FRAME;
    123     }
    124 
    125     // 操作码  
    126     uint16_t payloadLength = 0;
    127     uint8_t payloadFieldExtraBytes = 0;
    128     uint8_t opcode = static_cast<uint8_t>(frameData[0] & 0x0f);
    129     if (opcode == WS_TEXT_FRAME)
    130     {
    131         // 处理utf-8编码的文本帧  
    132         payloadLength = static_cast<uint16_t>(frameData[1] & 0x7f);
    133         if (payloadLength == 0x7e)
    134         {
    135             uint16_t payloadLength16b = 0;
    136             payloadFieldExtraBytes = 2;
    137             memcpy(&payloadLength16b, &frameData[2], payloadFieldExtraBytes);
    138             payloadLength = ntohs(payloadLength16b);
    139         }
    140         else if (payloadLength == 0x7f)
    141         {
    142             // 数据过长,暂不支持  
    143             ret = WS_ERROR_FRAME;
    144         }
    145     }
    146     else if (opcode == WS_BINARY_FRAME || opcode == WS_PING_FRAME || opcode == WS_PONG_FRAME)
    147     {
    148         // 二进制/ping/pong帧暂不处理  
    149     }
    150     else if (opcode == WS_CLOSING_FRAME)
    151     {
    152         ret = WS_CLOSING_FRAME;
    153     }
    154     else
    155     {
    156         ret = WS_ERROR_FRAME;
    157     }
    158 
    159     // 数据解码  
    160     if ((ret != WS_ERROR_FRAME) && (payloadLength > 0))
    161     {
    162         // header: 2字节, masking key: 4字节  
    163         const char *maskingKey = &frameData[2 + payloadFieldExtraBytes];
    164         char *payloadData = new char[payloadLength + 1];
    165         memset(payloadData, 0, payloadLength + 1);
    166         memcpy(payloadData, &frameData[2 + payloadFieldExtraBytes + 4], payloadLength);
    167         for (int i = 0; i < payloadLength; i++)
    168         {
    169             payloadData[i] = payloadData[i] ^ maskingKey[i % 4];
    170         }
    171 
    172         outMessage = payloadData;
    173         delete[] payloadData;
    174     }
    175 
    176     return ret;
    177 }
    178 
    179 int CWebSocketProtocol::wsEncodeFrame(string inMessage, string &outFrame, enum WS_FrameType frameType)
    180 {
    181     int ret = WS_EMPTY_FRAME;
    182     const uint32_t messageLength = inMessage.size();
    183     if (messageLength > 32767)
    184     {
    185         // 暂不支持这么长的数据
    186         std::cout << "暂不支持这么长的数据" << std::endl;
    187 
    188         return WS_ERROR_FRAME;
    189     }
    190 
    191     uint8_t payloadFieldExtraBytes = (messageLength <= 0x7d) ? 0 : 2;
    192     // header: 2字节, mask位设置为0(不加密), 则后面的masking key无须填写, 省略4字节  
    193     uint8_t frameHeaderSize = 2 + payloadFieldExtraBytes;
    194     uint8_t *frameHeader = new uint8_t[frameHeaderSize];
    195     memset(frameHeader, 0, frameHeaderSize);
    196     // fin位为1, 扩展位为0, 操作位为frameType  
    197     frameHeader[0] = static_cast<uint8_t>(0x80 | frameType);
    198 
    199     // 填充数据长度  
    200     if (messageLength <= 0x7d)
    201     {
    202         frameHeader[1] = static_cast<uint8_t>(messageLength);
    203     }
    204     else
    205     {
    206         frameHeader[1] = 0x7e;
    207         uint16_t len = htons(messageLength);
    208         memcpy(&frameHeader[2], &len, payloadFieldExtraBytes);
    209     }
    210 
    211     // 填充数据  
    212     uint32_t frameSize = frameHeaderSize + messageLength;
    213     char *frame = new char[frameSize + 1];
    214     memcpy(frame, frameHeader, frameHeaderSize);
    215     memcpy(frame + frameHeaderSize, inMessage.c_str(), messageLength);
    216     frame[frameSize] = '';
    217     outFrame = frame;
    218 
    219     delete[] frame;
    220     delete[] frameHeader;
    221     return ret;
    222 }

    参考资料:http://www.cnblogs.com/jice1990/p/5435419.html

        http://blog.csdn.net/grafx/article/details/54234518#

    项目下载

  • 相关阅读:
    P6007 [USACO20JAN]Springboards G
    CF1000F One Occurrence
    P6100 [USACO19FEB]Painting the Barn G
    P5838 [USACO19DEC]Milk Visits G
    P4085 [USACO17DEC]Haybale Feast
    P4267 [USACO18FEB]Taming the Herd
    P1712 [NOI2016]区间
    CF786B Legacy
    P5665 划分
    NOI2020网上同步赛 游记
  • 原文地址:https://www.cnblogs.com/fyluyg/p/6534574.html
Copyright © 2020-2023  润新知