c++服务器websocket支持

介绍

目前公司服务器是c++ tcp的网络架构,现在想用这套做h5游戏,所以要扩展支持websocket通信。

那么什么是websocket?它和tcp有什么区别?这些随便一搜一大把,这里就不再科普达。通俗简单点讲websocket就是山寨版的tcp,它底层实现就是tcp,唯一的区别就是网络传输时websocket协议前面多了个标志它的包头信息。去掉前面这部分包头剩下的就和普通tcp一样了。

那么讲到这里,在现有tcp上怎么扩展支持websocket?其实就很简单呢!既然它和tcp协议上就只是多了个包头部分,那么我们的任务其实主要就是怎么解析这个包头信息了。

最后,还有一个需要注意的细节是websocket协议前后端建立连接前需要一次握手协议,触发时机是client发起connet连接请求时,会向server发送这条握手的协议,server收到后要回复client,这样就建立了连接了。

好~闲话少说,下面直接上实现代码…

实现

1.握手。 
client第一次connet连接会发起握手协议,server在recv接收处解析,判断如果是websocket的握手协议,那么同样组装好特定格式包头回复给client,建立连接。

  • 判断是不是websocket协议
bool isWSHandShake(std::string &request)
{
    size_t i = request.find("GET");
    if(i == std::string::npos){ return false; } return true; }
  • 如果是,解析握手协议重新组装准备send回复给client
bool wsHandshake(std::string &request, std::string &response) { //得到客户端请求信息的key std::string tempKey = request; size_t i = tempKey.find("Sec-WebSocket-Key"); if(i == std::string::npos){ return false; } tempKey = tempKey.substr(i + 19, 24); //拼接协议返回给客户端 response = "HTTP/1.1 101 Switching Protocols\r\n"; response += "Connection: upgrade\r\n"; response += "Sec-WebSocket-Accept: "; std::string realKey = tempKey; realKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; SHA1 sha; unsigned int message_digest[5]; sha.Reset(); sha << realKey.c_str(); sha.Result(message_digest); for (int i = 0; i < 5; i++) { message_digest[i] = htonl(message_digest[i]); } realKey = base64_encode(reinterpret_cast<const unsigned char*>(message_digest), 20); realKey += "\r\n"; response += realKey.c_str(); response += "Upgrade: websocket\r\n\r\n"; return true; }

2.接收client协议解析

  • 首先解析包头信息
bool wsReadHeader(const unsigned char* cData, WebSocketStreamHeader* header)  
{  
    if (cData == NULL) return false; const unsigned char *buf = cData; header->fin = buf[0] & 0x80; header->masked = buf[1] & 0x80; unsigned char stream_size = buf[1] & 0x7F; header->opcode = buf[0] & 0x0F; if (header->opcode == WS_FrameType::WS_CONTINUATION_FRAME) { //连续帧 return false; } else if (header->opcode == WS_FrameType::WS_TEXT_FRAME) { //文本帧 } else if (header->opcode == WS_FrameType::WS_BINARY_FRAME) { //二进制帧 } else if (header->opcode == WS_FrameType::WS_CLOSING_FRAME) { //连接关闭消息 return true; } else if (header->opcode == WS_FrameType::WS_PING_FRAME) { // ping return false; } else if (header->opcode == WS_FrameType::WS_PONG_FRAME) { // pong return false; } else { //非法帧 return false; } if (stream_size <= 125) { // small stream header->header_size =6; header->payload_size = stream_size; header->mask_offset = 2; } else if (stream_size == 126) { // medium stream header->header_size = 8; unsigned short s = 0; memcpy(&s, (const char*)&buf[2], 2); header->payload_size = ntohs(s); header->mask_offset = 4; } else if (stream_size == 127) { unsigned long long l = 0; memcpy(&l, (const char*)&buf[2], 8); header->payload_size = l; header->mask_offset = 10; } else { //Couldnt decode stream size 非法大小数据包 return false; } /* if (header->payload_size > MAX_WEBSOCKET_BUFFER) { return false; } */ return true; }
  • 然后根据包头解析出真是数据
bool wsDecodeFrame(const WebSocketStreamHeader& header, unsigned char cbSrcData[], unsigned short wSrcLen, unsigned char cbTagData[]) { const unsigned char *final_buf = cbSrcData; if (wSrcLen < header.header_size + 1) { return false; } char masks[4]; memcpy(masks, final_buf + header.mask_offset, 4); memcpy(cbTagData, final_buf + header.mask_offset + 4, header.payload_size); for (INT_PTR i = 0; i < header.payload_size; ++i){ cbTagData[i] = (cbTagData[i] ^ masks[i % 4]); } //如果是文本包,在数据最后加一个结束字符“\0” if (header.opcode == WS_FrameType::WS_TEXT_FRAME) cbTagData[header.payload_size] = '\0'; return true; } 

3.组装server发给client协议

bool wsEncodeFrame(std::string inMessage, std::string &outFrame, enum WS_FrameType frameType) { const uint32_t messageLength = inMessage.size(); if (messageLength > 32767) { // 暂不支持这么长的数据 return false; } 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); outFrame = std::string(frame, frameSize); delete[] frame; delete[] frameHeader; return true; }

至此,tcp上扩展websocket所需要处理的3大块就都完成了,即握手、接收解析、发送组装。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值