fix(websocket): 修复WebSocket客户端线程安全与稳定性问题#5
Merged
cyanray merged 4 commits intocyanray:mainfrom Feb 15, 2026
Merged
Conversation
- 修复`select error`报错:`select()`返回错误后不再继续操作已关闭的socket - `status`改为`std::atomic`,消除主线程与接收线程之间的数据竞争 - 发送操作加`std::mutex`保护,防止多线程发送导致帧交错损坏 - 接收线程不再`detach()`改为`join()`,修复析构时use-after-free - `Shutdown()`与`RecvLoop`使用原子操作竞争socket关闭权,防止double-close - 修复payload length 127的字节序解析错误(小端架构) - mask key改为每帧随机生成,符合RFC 6455规范
- 避免从接收循环线程内部调用Shutdown时出现join自身的情况- 添加线程ID检查以区分当前线程是否为接收线程- 在回调中触发重连时自动分离线程而不是尝试join - 防止主线程和接收线程之间的潜在死锁情况
Owner
|
感谢你的 PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
修复了多个线程安全问题、socket 生命周期管理问题和 RFC 6455 合规性问题。
Changes
1. 线程安全 (Thread Safety)
status改为std::atomic<Status>— 原来status在主线程和RecvLoop线程之间存在数据竞争(datarace),属于未定义行为
std::mutex sendMutex保护发送操作 — 多线程同时调用SendText/SendBinary等方法时,底层send()调用可能交错,导致 WebSocket 帧损坏
Shutdown()与RecvLoop使用status.exchange()原子竞争 — 确保只有一方负责关闭 socket,防止 double-close2. 线程生命周期管理 (Thread Lifecycle)
detach(),改为join()— 原来 detach 后无法等待线程退出,析构函数中delete PrivateMembers时线程可能仍在访问,导致 use-after-free
Shutdown()再delete PrivateMembers— 确保线程结束后才释放资源Shutdown()无论谁关了 socket 都执行join()— 防止std::thread析构时因仍 joinable 而触发std::terminate3. 修复 "select error" (Fix select error)
select()返回 -1 时直接break退出循环 — 原来只调用ErrorCallback但不中断,代码继续执行到recv(),对已关闭的 socket 操作导致未定义行为select()返回 0(超时)时明确continue— 原来超时后会穿透到帧解析逻辑4. RFC 6455 合规性
{0xd2, 0x28, 0xb6, 0xde},RFC 6455 Section 5.3要求客户端每帧使用不可预测的 mask key
memcpy直接拷贝uint64_t,在小端架构(x86/ARM)上字节序错误。改为手动按大端(网络字节序)逐字节解析5. 其他改进
Connect()握手失败或无响应时清理 socket,防止泄漏recvbuffer 从 2048 扩大到 4096frame_data.reserve()从固定 2048 改为length + 14(按实际需要预分配)TryParseFrame中增加 mask key 读取前的长度检查,防止越界