C++实现WebSocket简单服务器

2023-11-08

参考链接:
链接1
链接2
链接3

WebSocket简介

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 与 HTTP/2 一样,其实都是为了解决 HTTP/1.1 的一些缺陷而诞生的,而 WebSocket 针对的就是「请求-应答」这种"半双工"的模式的通信缺陷。

「请求-应答」是"半双工"的通信模式,数据的传输必须经过一次请求应答,这个完整的通信过程,通信的同一时刻数据只能在一个方向上传递。它最大的问题在于,HTTP 是一种被动的通信模式,服务端必须等待客户端请求才可以返回数据,无法主动向客户端发送数据。

这也导致在 WebSocket 出现之前,一些对实时性有要求的服务,通常是基于轮询(Polling)这种简单的模式来实现。轮询就是由客户端定时发起请求,如果服务端有需要传递的数据,可以借助这个请求去响应数据。轮询的缺点也非常明显,大量空闲的时间,其实是在反复发送无效的请求,这显然是一种资源的损耗。

创建WebSocket服务器的一般步骤

  1. 创建一个服务器监听
  2. 开始接收数据,此时开始接收的数据主要是客户端发出的协议握手报文,报文内容大概如下:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

其中

  • | Sec-WebSocket-Key |, 必传, 由客户端随机生成的 16 字节值, 然后做 base64 编码, 客户端需要保证该值是足够随机, 不可被预测的 (换句话说, 客户端应使用熵足够大的随机数发生器), 在 WebSocket 协议中, 该头部字段必传, 若客户端发起握手时缺失该字段, 则无法完成握手
  • | Sec-WebSocket-Version |, 必传, 指示 WebSocket 协议的版本, RFC 6455 的协议版本为 13, 在 RFC 6455 的 Draft 阶段已经有针对相应的 WebSocket 实现, 它们当时使用更低的版本号, 若客户端同时支持多个 WebSocket 协议版本, 可以在该字段中以逗号分隔传递支持的版本列表 (按期望使用的程序降序排列), 服务端可从中选取一个支持的协议版本
  • | Sec-WebSocket-Protocol |, 可选, 客户端发起握手的时候可以在头部设置该字段, 该字段的值是一系列客户端希望在于服务端交互时使用的子协议 (subprotocol), 多个子协议之间用逗号分隔, 按客户端期望的顺序降序排列, 服务端可以根据客户端提供的子协议列表选择一个或多个子协议
  • | Sec-WebSocket-Extensions |, 可选, 客户端在 WebSocket 握手阶段可以在头部设置该字段指示自己希望使用的 WebSocket 协议拓展
  1. 根据接收到的数据(就是上面的报文),可以先判断一下是不是握手协议(这里可以直接判断一下recv从缓冲区读到的数据是否包含“GET”),对这个报文进行解析,获取其中的Sec-WebSocket-Key(也就是这里的dGhlIHNhbXBsZSBub25jZQ==)。然后服务端要对这个key(包含24个字符)进行解析,解析完成后会得到一个密码,如果服务器发给客户端的报文密码一致的话,此时就完成了协议握手,然后现在就已经成功创建了一个基于webosket协议的连接了。
    当握手成功后传入的报文信息如下:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13

  • | Sec-WebSocket-Accept |, 必传, 客户端发起握手时通过 | Sec-WebSocket-Key | 字段传递了一个将随机生成的 16 字节做 base64 编码后的字符串, 服务端若接收握手, 则应将该值与 WebSocket 魔数 (Magic Number) “258EAFA5-E914-47DA- 95CA-C5AB0DC85B11” 进行字符串连接, 将得到的字符串做 SHA-1 哈希, 将得到的哈希值再做 base64 编码, 最终的值便是该字段的值, 举例来说, 假设客户端传递的 Sec-WebSocket-Key 为 “dGhlIHNhbXBsZSBub25jZQ==”, 服务端应首先将该字符串与 WebSocket 魔数进行字符串拼接, 得到 “dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA- C5AB0DC85B11”, 然后对该字符串做 SHA-1 哈希运算得到哈希值 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea, 然后对该哈希值做 base64 编码, 最终得到 Sec-WebSocket-Accept 的值为 s3pPLMBiTxaQ9kYGzzhZRbK+xOo=, 当客户端收到服务端的握手响应后, 会做同样的运算来校验该值是否符合预期, 以便于判断服务端是否真的支持 WebSocket 协议, 设置这个环节的目的就是为了最终校验服务端对 WebSocket 协议的支持性, 因为单纯使用 Upgrade 机制, 对于一些没有正确实现 HTTP Upgrade 机制的 Web Server, 可能也会返回预期的 Upgrade, 但实际上它并不支持 WebSocket, 而引入 WebSocket 魔数并进行这一系列操作后可以很大程度上确定服务端确实支持 WebSocket 协议
  1. 最后,如果握手完成,服务端和客户端之间就可以传输数据了。此时传入的数据就是用WebSocket协议封装好的数据。

WebSocket协议解析

下面就来讲下WebSocket协议,协议格式如下:
在这里插入图片描述

第一个字节:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++实现WebSocket简单服务器 的相关文章

随机推荐

  • Datawhale 李宏毅机器学习 Task1

    目录 一 简单理解机器学习 二 机器学习基本步骤 三 机器学习相关步骤 1 监督学习 2 半监督学习 3 迁移学习 4 无监督学习 5 监督学习中的结构化学习 6 强化学习 四 学习总结 一 简单理解机器学习 就像是生物本能 比如河狸修水坝
  • Jmeter-验证码(图片base64加密+uuid)

    适用于验证码为img 图片base64加密 标识 uuid 的情况 1 先下载OcrServer图片识别工具 2 添加验证码获取的http请求 3 通过json提取器获取img和uuid 4 调用http请求获取OcrServer识别的结果
  • ovirt超整合部署记录

    网络磁盘设置 root ovirt106 ping www 163 com PING z163picipv6 v bsgslb cn 221 233 240 109 56 84 bytes of data 64 bytes from 221
  • 【前端】neo4j导出json数据可视化

    占位贴 提醒自己有时间了把源码和思路提供一下
  • ArcgisOpr CXX0030

    这个错误我是找了好多天才找到了 AE ArcgisEngine 在用VC环境进行开发时 对license的初始化失败 并在VC的编译输出窗口中提示Could not bind to a valid ArcGIS installation 是
  • UnitTest单元测试框架解析【实用篇】

    UnitTest是展开自动化测试的基础 这个框架很重要 首先我们先自己写一个测试类 1 被测试类 Widthget py coding utf 8class Widthget def init self size 10 10 self si
  • 常用的正则表达式总结(慢慢增加中。。。)

    1 0 100 内的数字 不包含0 100 排除0 0 0 00 保留三位小数 1 9 1 2 d 1 3 0 0 9 1 2 1 9 2 0 100 内的数字包含0 100 保留三位小数 d 1 2 d 1 3 100
  • Java将jar包打成exe包

    如何获取jar包 1 如果是maven项目 2 如果是SpringBoot项目 添加maven插件 直接使用maven插件进行打包 Jar打包成exe 准备 相关的jar Exe4j应用程序 地址 https www ej technolo
  • 第三届Python数据分析职业技能比赛A题

    第三届Python数据分析职业技能比赛A题 Hello World 赛题 竞赛背景 字段说明 考核目标 任务 任务一 数据预处理 任务二 数据可视化 任务三 数据分析 任务一思路 1 2 1 3 任务二思路 2 1 2 2 2 3 任务三思
  • 禅道程序员的10条原则

    在一个阴雨的早上 我坐在桌子旁 开始想如何才能高效的工作 在我成为一个自由职业者之前 我有很长一段时间都很努力工作 但收效甚微 我在2006开始接触禅学 我马上意识到 古代的禅宗大师们几百年前早就已经知道现今的程序员应该如何工作 虽然我很讨
  • 如何通过官方渠道下载任意版本的Spring相关的jar包

    1 进入官网http spring io 2 第二步 点击PROJECTS 3 点击SPRING FRAMEWORK 4 点击上一步中GitHub图标 进入下面的页面 第五步 把第四步出现的页面往下拉 找到 Spring Framework
  • python Matplotlib画图之调整字体大小的示例

    本文来源于公众号 csdn2299 喜欢可以关注公众号 程序员学府 本篇文章主要介绍了python Matplotlib画图之调整字体大小的示例 小编觉得挺不错的 现在分享给大家 也给大家做个参考 一起跟随小编过来看看吧 一张字体调整好的示
  • 不能在slot上绑定和触发事件

    在 slot 上进行事件的监听和分发 这是不可能的 组件的 slot 由调用它的父组件提供 这意味着所有事件都应该与父组件相关联 尝试去倾听这些变化意味着你的父子组件是紧密耦合的 可以使用 parent 来操作 div div
  • 5.1广度优先遍历的递归与迭代实现;

    队列先进先出的性质 符合 广度优先遍历时 一层一层的遍历逻辑 lc102 102 二叉树的层序遍历 107 二叉树的层次遍历II 199 二叉树的右视图 637 二叉树的层平均值 429 N叉树的层序遍历 515 在每个树行中找最大值 11
  • 谭铁牛:人工智能 找风口不如找关口

    不过我们不能光打打嘴炮 如何克服困难和挑战 让人工智能帮到你的工作 你的事业呢 让我们将李开复的演讲内容 再结合一个实例 来给大家解释一下 现在 假设你是一个程序员 虽然哥也是一媒体人 但黑起自己的行业来是丝毫不会手软的 假设你现在是一家媒
  • Python库

    库名称简介 Chardet字符编码探测器 可以自动检测文本 网页 xml的编码 colorama主要用来给文本添加各种颜色 并且非常简单易用 Prettytable主要用于在终端或浏览器端构建格式化的输出 difflib Python 标准
  • openwrt添加自己的应用程序(SDK下编译模块)出现的问题

    openwrt 版本 15 05 CC 最近在openwrt里面想编写一个串口的读写程序 没想到会出现以下问题 1 编译的时候 以下为网友遇到的问题 Package helloworld is missing dependencies fo
  • GetProcAddress()方法返回NULL值的问题

    使用动态加载的方式使用动态库 loadlibrary 成功加载动态库 之后使用GetProcAddress 方法得到函数指针却返回空值 使用GetLastError 方法得到错误代码127 出现此错误的原因一般是要加载的函数名称与动态库中函
  • 外部中断原理

    外部中断 当CPU正在按主程序运行时 外部发生了紧急事件 向CPU发送中断请求来优先处理紧急事件 当CPU处理完紧急事件后再继续从主程序断开的地方运行程序 发出中断请求的源称为中断源 不同的中断源具有不同的优先级别 当CPU同时接收到多个中
  • C++实现WebSocket简单服务器

    参考链接 链接1 链接2 链接3 WebSocket简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议 WebSocket使得客户端和服务器之间的数据交换变得更加简单 允许服务端主动向客户端推送数据 在WebSocket