11.2.3 固定话题聊天实现
1 Websocket连接实现
CWebSocketConn继承CHttpConn
大体的框图(代码实现时 函数命名有部分差异)
2实现登录历史消息拉取
见int ApiGetRoomHistory(Room &room, MessageBatch &message_batch); 函数的实现。
3 实现客户端发送消息到服务端
见void CWebSocketConn::sendHelloMessage()函数。
4 实现服务端广播消息给客户端
见int CWebSocketConn::handleClientMessage(Json::Value &root)函数。
5 websocket简单参考
WebSocket 协议是一种基于 TCP 的双向通信协议,允许客户端和服务器在单个连接上进行全双工通信。以 下是 WebSocket 协议的完整解析,包括握手、数据帧格式、心跳机制和关闭流程。
5.1. WebSocket 握手(Handshake)
WebSocket 握手是基于 HTTP 协议的,客户端通过发送一个 HTTP 升级请求来建立 WebSocket 连接。
客户端握手请求
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- Upgrade: 指定协议为 websocket 。
- Connection: 值为 Upgrade ,表示请求协议升级。
- Sec-WebSocket-Key: 客户端随机生成的 Base64 编码字符串,用于服务器验证。
- Sec-WebSocket-Version: WebSocket 协议版本,通常为 13 。
服务器握手响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- 101 Switching Protocols: 表示协议切换成功。
- Sec-WebSocket-Accept: 服务器对客户端 Sec-WebSocket-Key 进行处理后返回的值,计算公式为:
Sec-WebSocket-Accept = base64(sha1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CAC5AB0DC85B11"))
5.2. WebSocket 数据帧格式
WebSocket 数据帧是 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 ... |
+---------------------------------------------------------------+
字段说明
1. FIN (1 bit): 表示是否是消息的最后一帧。如果为 1 ,则表示是最后一帧。
2. RSV1, RSV2, RSV3 (各 1 bit): 保留字段,必须为 0 ,除非扩展协议定义了非零值。
3. Opcode (4 bits): 定义帧的类型:
- 0x0 : 连续帧(continuation frame)
- 0x1 : 文本帧(text frame)
- 0x2 : 二进制帧(binary frame)
- 0x8 : 关闭帧(connection close)
- 0x9 : Ping 帧 0xA : Pong 帧
4. Mask (1 bit): 表示是否对数据进行了掩码处理。客户端发送的数据必须掩码( 1 ),服务器发送的数据不能掩 码( 0 )。
5. Payload Length (7 bits): 数据长度:
- 如果值为 0-125 ,表示数据长度。
- 如果值为 126 ,则后续 2 字节表示长度。
- 如果值为 127 ,则后续 8 字节表示长度。
6. Masking-Key (4 bytes): 如果 Mask 为 1 ,则包含 4 字节的掩码键,用于解码数据。
7. Payload Data: 实际传输的数据。
5.3. WebSocket 心跳机制(Ping/Pong)
WebSocket 通过 Ping 和 Pong 帧实现心跳机制,用于检测连接是否存活。
Ping 帧: 由客户端或服务器发送,用于检测对方是否在线。 Opcode 为 0x9 。
Pong 帧: 作为对 Ping 帧的响应,表示连接正常。 Opcode 为 0xA 。
5.4. WebSocket 关闭连接
WebSocket 通过关闭帧(Close Frame)来优雅地关闭连接。
关闭帧: Opcode 为 0x8 。 可以包含一个关闭状态码和原因(可选)。 状态码示例:\
- 1000 : 正常关闭。
- 1001 : 端点离开。
- 1002 : 协议错误。
- 1003 : 不支持的数据类型。
- 1008 (Policy Violation): 策略违反,表示数据或行为违反了某些策略。
5.5. WebSocket 协议流程总结
1. 握手: 客户端发送 HTTP 升级请求。 服务器响应 101 状态码,完成握手。
2. 数据传输: 客户端和服务器通过数据帧(Text/Binary Frame)传输数据。
3. 心跳检测: 通过 Ping/Pong 帧维持连接。
4. 关闭连接: 通过 Close Frame 优雅关闭连接。
5.6. WebSocket 协议示例
客户端发送文本消息
1. 客户端发送数据帧: FIN: 1 Opcode: 0x1 (文本帧) Payload: Hello, Server!
2. 服务器接收并处理消息。
服务器发送 Pong 帧
1. 客户端发送 Ping 帧。 2. 服务器回复 Pong 帧。
关闭连接
1. 客户端发送 Close Frame。 2. 服务器回复 Close Frame,关闭连接。
参考链接:0voice · GitHub