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 != "\r") 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\r\n"; 74 response += "Connection: upgrade\r\n"; 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 += "\r\n"; 91 response += "Upgrade: websocket\r\n\r\n"; 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] = '\0'; 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#