From: https://blog.csdn.net/u010223072/article/details/80637127

介绍

目前公司服务器是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大块就都完成了,即握手、接收解析、发送组装。

c++服务器websocket支持相关推荐

  1. 【Go】gim IM服务器,支持多业务接入

    地址 https://github.com/alberliu/gim 说明 gim是一个即时通讯服务器,代码全部使用golang完成.主要功能: 支持tcp,websocket接入 离线消息同步 多业 ...

  2. Windows2003服务器不支持FLV视频的解决方法

    Windows2003服务器不支持FLV视频的解决方法 2007年10月19日 星期五 10:43 A.M. 原因:WIN2003加强了IIS6的MIME验证,一切未注册扩展文件格式统统显示404错误 ...

  3. Linux下SVN服务器同时支持Apache的http和https及svnserve独立服务器三种模式且使用相同的访问权限账号...

    1.在服务器上安装配置SVN服务: 2.配置SVN服务同时支持Apache的http和svnserve独立服务器两种模式访问: 3.Apache的http和svnserve独立服务器两种模式使用相同的 ...

  4. Nginx服务器不支持PATH_INFO的问题及解决办法

    Nginx服务器不支持PATH_INFO的问题及解决办法 参考文章: (1)Nginx服务器不支持PATH_INFO的问题及解决办法 (2)https://www.cnblogs.com/echoso ...

  5. 到底一台服务器能够支持多少TCP并发连接?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 朱小厮 来源 | 公众号「朱小厮的博客」 曾几 ...

  6. 服务器支持curl,互联网要点:服务器不支持curl_exec的解决办法

    当下应该属于互联网时代吧今天小编分享一篇有关于服务器不支持curl_exec的解决办法,相信小伙伴们对这个话题应该也很关注吧,那么现在就为小伙伴们说说服务器不支持curl_exec的解决办法,小编也收 ...

  7. 十三、Tomcat的WebSocket支持

    十三.Tomcat的WebSocket支持 转载于:https://www.cnblogs.com/ljiwej/p/7280404.html

  8. linux curl命令验证服务器断点续传支持

    有个同事说,发现现在对外下载安装包的服务器不支持断点续传,我听了一阵纳闷,lighttpd server对于静态文件应该默认支持断点续传的,登机器查看lighttpd配置文件发现 对断点续传的支持被禁 ...

  9. MySQL兼容服务器_服务器不支持 MySql 数据库的解决方法

    php.ini文件没有参数没有配置正确 解决方法: (1) 打开php.ini文件,找到: extension_dir = "./" 将其改为: extension_dir = & ...

最新文章

  1. centos iptables关于ping
  2. Win32 控件篇(3)
  3. 匹配替换指定文本为html标签
  4. Spring mvc HandlerMapping 实现机制
  5. 智能卡门禁管理系统_出入口门禁控制系统与消防火灾报警系统怎么联动?
  6. b+树时间复杂度_满二叉树、完全二叉树、二叉搜索树、平衡二叉树
  7. sqlalchemy_外键连接表
  8. 人群与网络:新事物在网络中的扩散
  9. Bootstrap Paginator 分页插件参数介绍及使用
  10. 分支限界算法c语言_算法分析
  11. JUCE学习笔记07-自定义正弦振荡器类
  12. ShadowGun之Shader分析
  13. 未来生活进行时: 畅想未来新兴技术40年——百大趋势性技术汇总(中)
  14. python:Craps赌博游戏
  15. 新疆上半年工业品价格总水平创十七年新低
  16. java定义个字符串_Java定义字符串(2种方式)
  17. HEU Monkey CC
  18. QQ群78928780记录整理:90514技术话题-部分
  19. 跨考计算机408的学习心得技巧分享
  20. vue中路由按需加载的几种方式

热门文章

  1. C#实现Combobox自动匹配字符
  2. 前端招聘与前端卖身的困境
  3. RSA客户端js加密服务器C#解密(含源码)
  4. 计算机 编程 教程 pdf,计算机专业教程-第3章编程接口介绍.pdf
  5. opencv:卷积涉及的基础概念,Sobel边缘检测代码实现及卷积填充模式
  6. 2021-08-25556. 下一个更大元素 III
  7. fcn从头开始_如何使用Go从头开始构建区块链
  8. 如何使用React,TypeScript和React测试库创建出色的用户体验
  9. 机器学习 美股_我如何使用机器学习来探索英美文学之间的差异
  10. centos7 nginx+php5.6+mysql安装与配置