基于QT 6.6.0 WebSocket协议进行客户端和服务端的通信(C/C++)

该文章已生成可运行项目,

视频讲解1:https://www.bilibili.com/video/BV1j3p7zEE3p/?spm_id_from=333.1387.homepage.video_card.click&vd_source=b2eaaddb2c69bf42517a2553af8444ab

视频讲解2:https://www.douyin.com/collection/7545384881891575854/1

代码下载地址1:https://github.com/KeepTryingTo/QtWebSocket_client_server/tree/main

代码下载地址2:https://github.com/KeepTryingTo/QT_Learning/tree/main/QtWebSocket

相关链接:QT6.6.0基于QTcpSocket和QTcpServer实现多个客户端群聊

目录

TCP协议

基于TCP协议三次握手

基于socket的连接过程

HTTP协议连接过程

第一步:Client → Server(ClientHello)协商加密参数,为密钥交换做准备

第二步:Server → Client(ServerHello + Certificate + ServerKeyExchange + ServerHelloDone)证书验证是信任链建立的核心

​​第三步:Client → Server(ClientKeyExchange + ChangeCipherSpec + Finished)

第四步:Server → Client(ChangeCipherSpec + Finished)​

websocket协议

websocket协议格式

websocket连接握手建立

客户端请求消息

服务端响应消息

数据帧的传输

保持连接和心跳

连接释放

其中涉及的安全验证机制

websocket协议和http协议比较

QT 6.6.0中WebSocket使用

客户端页面

服务端页面

操作流程

客户端和服务端整体通信流程

漂亮的图标网站

参考链接

QT应用案例汇总


        大家已经对TCP协议和http协议有所了解,并且前面我们还将SSL/TLS协议应用到我们的高性能webserver项目中,保证传输数据加密。但是后面我们还是会提及TCP协议和http协议,因为websocket协议是建立在TCP三次握手协议基础上来进行的,和http协议也相关,是因为数据传输过程中是从http协议升级到websocket协议。

TCP协议

基于TCP协议三次握手

基于socket的连接过程

图像来源

HTTP协议连接过程

        由于http协议是建立在tcp三次握手的基础上的,因此首先了解tcp三次握手之后来了解http协议更好,关于http中的ssl/tls协议的四次握手过程之前大致讲过,建议还是大家仔细把这部分好好理解和梳理一下。

TinyWebServer-v2服务器新增SSL/TLS协议和Content-Encoding字段指定压缩格式,生成私钥和自签证书以及数据压缩,保证数据在传输的过程中是加密和提高传输的效率

linux上使用tcpdump工具抓包(基于TCP协议的客户端向服务端发送信息,以及使用SSL/TLS协议之后客户端向服务端发送信息)和wireshark工具分析抓包(linux/C/C++)

我们这里只看SSL四次握手,前面的TCP三次握手跳过:

第一步:Client → Server(ClientHello)协商加密参数,为密钥交换做准备

+-----------------------------+
|        ClientHello          |
+-----------------------------+
| Version       | TLS 1.2     |
| Random        | 32字节随机数 |
| Cipher Suites | 支持的加密套件|
| Session ID    | (可选)       |
+-----------------------------+

第二步:Server → Client(ServerHello + Certificate + ServerKeyExchange + ServerHelloDone)证书验证是信任链建立的核心

+-----------------------------------+
|           ServerHello             |
+-----------------------------------+
| Version       | 选定TLS版本       |
| Random        | 32字节服务器随机数 |
| Cipher Suite  | 选定加密套件      |
| Session ID    | (如需恢复)        |
+-----------------------------------+
|           Certificate             |
+-----------------------------------+
| 服务器证书链(包含公钥)          |
+-----------------------------------+
|       ServerKeyExchange (ECDHE密钥交换算法)    |
+-----------------------------------+
| DH/ECDH参数(若证书不含密钥信息) |
+-----------------------------------+
|        ServerHelloDone            |
+-----------------------------------+

​第三步:Client → Server(ClientKeyExchange + ChangeCipherSpec + Finished)

+-----------------------------------+
|        ClientKeyExchange          |
+-----------------------------------+
| 预主密钥(Pre-Master Secret)      |
| 用服务器公钥加密后传输            |
+-----------------------------------+
|        ChangeCipherSpec           |
+-----------------------------------+
| 通知:后续消息启用协商的加密参数   |
+-----------------------------------+
|           Finished                |
+-----------------------------------+
| 加密的握手完整性验证(HMAC)       |
+-----------------------------------+

第四步:Server → Client(ChangeCipherSpec + Finished)​

+-----------------------------------+
|        ChangeCipherSpec           |
+-----------------------------------+
| 通知:后续消息启用加密             |
+-----------------------------------+
|           Finished                |
+-----------------------------------+
| 加密的握手完整性验证(HMAC)       |
+-----------------------------------+

websocket协议

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

websocket协议格式

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
  • ​FIN(1 bit)​​:指示是否为消息的最后一个片段;

  • ​RSV1, RSV2, RSV3(各1 bit)​​:保留位,用于扩展,默认为0;

  • ​Opcode(4 bits)​​:定义帧的类型(如文本、二进制、控制帧等);

  • ​Mask(1 bit)​​:指示负载是否被掩码(客户端到服务器的帧必须掩码);

  • ​Payload length(7/7+16/7+64 bits)​​:负载长度(可能扩展);

  • ​Masking-key(0或4字节)​​:如果Mask位为1,则包含32位掩码键;

  • ​Payload data​​:实际传输的数据(可能包含扩展数据和应用数据)。

websocket连接握手建立

WebSocket连接通过HTTP升级请求建立(RFC 6455 Section 4)

客户端请求消息
GET /chat HTTP/1.1 #请求行
Host: server.example.com #请求头中的服务域名(地址)
Upgrade: websocket # 告诉服务端升级为websocket
Connection: Upgrade # 连接升级
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Connection: Upgrade

  • 作用​​:指示该连接需要“升级”到其他协议(RFC 7230 Section 6.7)。
  • Upgrade关系​​:两者必须同时存在,否则服务器会拒绝。

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

import base64, os key = base64.b64encode(os.urandom(16)) # 16字节随机数→Base64

​作用​​:提供随机生成的Base64编码的16字节密钥(RFC 6455 Section 4.1)。

  • ​服务器处理​​:服务器需将此密钥与固定GUID拼接后计算SHA-1哈希,生成Sec-WebSocket-Accept响应头。
  • ​安全性​​:防止意外或恶意的连接升级(如缓存污染攻击)。

Origin: http://example.com

  • 作用​​:标识请求来源(协议+域名+端口),用于同源策略检查(RFC 6455 Section 10.2)。
  • ​必要性​​:浏览器必送,非浏览器客户端可省略。
  • 安全意义​​:服务器可据此拒绝跨域请求。

Sec-WebSocket-Protocol: chat, superchat

  • 作用​​:指定子协议(应用层协议),服务器需选择其中一个返回(RFC 6455 Section 4.1)。
  • 示例​​:若服务器选择chat,则响应头中返回Sec-WebSocket-Protocol: chat
  • 用途​​:区分同一WebSocket端点的不同业务逻辑。

Sec-WebSocket-Version: 13

  • 作用​​:声明客户端支持的WebSocket协议版本(RFC 6455 Section 4.1)。
  • ​关键值​​:13是当前标准版本,其他版本(如7、8)已废弃。

  1. 为什么需要掩码(Masking)​​(RFC 6455 Section 10.3):仅客户端→服务器的帧需要掩码,防止恶意脚本通过代理缓存攻击。
  2. 握手失败场景​​:
    • 如果服务器不支持WebSocket,会返回非101状态码(如200或404)。
    • 如果Sec-WebSocket-Version不匹配,服务器返回Sec-WebSocket-Version支持的版本列表。
服务端响应消息
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

其中Sec-WebSocket-Accept

  • ​服务器处理​​:服务器需将此密钥与固定GUID拼接后计算SHA-1哈希,生成Sec-WebSocket-Accept响应头。
  • ​安全性​​:防止意外或恶意的连接升级(如缓存污染攻击)。
import hashlib
base64 GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 
accept = base64.b64encode(hashlib.sha1(key + GUID.encode()).digest())

数据帧的传输

  • 数据可以分片(fragmentation)传输(RFC 6455 Section 5.4)。

  • 控制帧(如Ping/Pong/Close)可以穿插在数据帧中。
  • 客户端到服务器的帧必须掩码(防止代理缓存污染攻击),服务器到客户端的帧不需要掩码。

保持连接和心跳

  • WebSocket连接默认是持久化的。

  • 使用Ping/Pong帧作为心跳机制(RFC 6455 Section 5.5.2/5.5.3):
    • 服务器或客户端可以发送Ping帧,对方必须回复Pong帧。
    • Pong帧必须包含与Ping帧相同的应用数据(如果有)。
  • 心跳用于检测连接是否存活,避免超时断开。

连接释放

  • 通过交换Close帧来正常关闭连接(RFC 6455 Section 5.5.1):

    • 一端发送Close帧(可包含状态码和原因),另一端回复Close帧后关闭TCP连接。
    • 状态码定义在RFC 6455 Section 7.4(如1000表示正常关闭)。
  • 异常关闭(如TCP连接意外断开)不需要交换Close帧。

其中涉及的安全验证机制

  • Origin验证​​:服务器可以检查Origin头部防止跨站请求伪造(CSRF)。

  • 掩码机制​​:客户端到服务器的帧必须掩码,防止恶意脚本通过代理缓存攻击(RFC 6455 Section 10.3)。
  • TLS加密​​:推荐使用wss协议(WebSocket over TLS)保证数据机密性和完整性。
  • 协议限制​​:WebSocket握手是有效的HTTP请求,但后续数据帧不符合HTTP格式,避免被误解。

websocket协议和http协议比较

特性

HTTP

WebSocket

​连接方式​

短连接(请求-响应)

长连接(全双工)

​通信模式​

半双工(客户端发起请求)

全双工(双方可主动发送数据)

​头部开销​

每个请求/响应都包含完整头部

初始握手后,数据帧头部很小(2-14字节)

​服务端推送​

需要轮询或长轮询模拟

原生支持服务端主动推送

​协议升级​

通过HTTP Upgrade机制升级

​数据格式​

文本(HTTP报文)

二进制帧(可传输文本或二进制)

​适用场景​

网页浏览、API调用

实时通信(聊天、游戏、实时数据推送)

QT 6.6.0中WebSocket使用

客户端页面
 

服务端页面

操作流程

  1. 第一步:分别启动服务端和客户端,加载CSS文件渲染画面和看到聊天框中加载的主机信息(右下角的灯还是暗的)
  2. 第二步:点击服务端“监听”按钮(服务端右下角的灯开始黄颜色)
  3. 第三步:客户端点击“连接服务器”按钮(客户端右下角的灯开始黄颜色)
  4. 第四步:等待客户端和服务端连接成功(服务端和客户端右下角的灯开始绿色)
  5. 第五步:客户端和服务端分别可以向对方发送消息
  6. 第六步:客户端和服务端可以查看连接状况;
  7. 第七步:可以选择断开连接
  8. 第八步:可以清空聊天信息
  9. 第九步:服务端可以点击“发送文件”按钮,客户端接收完成可以点击“下载”按钮选择保存文件位置

客户端和服务端整体通信流程

+-------------------+                         +-------------------+
|      Client       |                         |      Server       |
+-------------------+                         +-------------------+
         |                                           |
         | 1. TCP连接 (WS握手)                        |
         |------------------------------------------>|
         |                                           |
         | 2. WebSocket连接建立成功                   |
         |<------------------------------------------|
         |                                           |
         | 3. 发送文本消息 "客户端已连接!"             |
         |------------------------------------------>|
         |                                           |
         | 4. 服务端响应文本消息                       |
         |<------------------------------------------|
         |                                           |
         | 5. 服务端点击"发送文件"按钮                 |
         |    服务端准备文件并发送FILE_HEADER          |
         |<------------------------------------------|
         | (包含文件名和大小)                          |
         |                                           |
         | 6. 客户端发送FILE_ACK确认                  |
         |------------------------------------------>|
         |                                           |
         | 7. 服务端开始分块发送FILE_CHUNK             |
         |<------------------------------------------|
         | (循环发送直到文件完成)                      |
         |                                           |
         | 8. 客户端对每个块回复FILE_CHUNK_ACK        |
         |------------------------------------------>|
         |                                           |
         | 9. 文件传输完成                            |
         |                                           |
         | 10. 客户端点击"下载"保存文件                |
         |                                           |
         | 11. 任意一方可发送文本消息                  |
         |<----------------------------------------->|
         |                                           |
         | 12. 断开连接                               |
         |<----------------------------------------->|
         |                                           |

漂亮的图标网站

https://unicode.org/emoji/charts/full-emoji-list.html

https://getemoji.com/

https://emojipedia.org/emoji-mashup/twitter/twemoji-14.0?a=%F0%9F%98%80&b=%F0%9F%98%86

参考链接

https://baike.baidu.com/item/WebSocket/1953845

https://doc.qt.io/qt-6/qwebsocketserver.html

https://blog.youkuaiyun.com/gitblog_06785/article/details/147667884

websocket文档对应的中文翻译

https://subingwen.cn/project/websocket/#2-2-4-%E5%85%B3%E9%97%AD%E8%BF%9E%E6%8E%A5

RFC6455文档(https://www.rfc-editor.org/search/rfc_search_detail.php

https://websocket.xiniushu.com/data-framing

QT应用案例汇总

https://mydreamambitious.blog.youkuaiyun.com/article/details/142690011?spm=1011.2415.3001.5331

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值