WebSocket.onopen回调内调用send()始终失败问题排查求助
WebSocketSharp服务端收不到onopen回调内发送消息的解决方案
问题根因
这个问题是WebSocketSharp库的历史遗留时序缺陷导致的:浏览器端触发onopen回调时,WebSocket的TCP连接和握手流程已经完成,客户端状态正常切换为OPEN,但WebSocketSharp服务端还未完成对应会话的接收流初始化操作。此时客户端发送的数据包要么被未就绪的服务端流丢弃,要么被识别为不完整的帧,触发你遇到的The header of a frame cannot be read from the stream异常。
手动加延时、点击按钮发送能正常工作,本质都是等服务端完成了流初始化的操作,窗口时间过了之后自然收发正常。
解决方案
方案1:双向握手确认(最稳定,无兼容问题)
在服务端完成会话初始化后,先向客户端推送一条就绪信号,客户端收到信号后再发送首条业务消息,完全规避时序问题:
- 服务端修改
TestWS类,重写OnOpen方法:
public class TestWS : WebSocketBehavior { protected override void OnOpen() { // 向客户端发送就绪确认信号,信号内容可以自定义 Send("__SERVER_INIT_COMPLETE__"); } protected override void OnMessage (MessageEventArgs e) { System.Diagnostics.Debug.WriteLine($"Client message: {e.Data}"); Send($"String length: {e.Data.Length} [msg received: {e.Data}]"); } }
- 客户端修改逻辑,收到就绪信号后再发送首条消息:
const webSocket = new WebSocket("ws://localhost:51036/test"); webSocket.onopen = e => { console.log("Socket connection opened!"); } webSocket.onmessage = e => { // 匹配服务端发来的就绪信号 if (e.data === "__SERVER_INIT_COMPLETE__") { // 此时服务端流已经就绪,可以正常发送消息 webSocket.send("hello :D"); return; } // 其他业务消息的处理逻辑 }
方案2:延时发送(临时调试用,不建议生产环境)
如果只是本地测试不想改太多逻辑,可以沿用你测试的setTimeout方案,但注意延时时长不固定,性能较差的设备可能需要调大延时数值,存在偶发失败概率:
webSocket.onopen = e => { setTimeout(() => { webSocket.send("hello :D"); }, 200); console.log("Socket connection opened!"); }
方案3:更换服务端实现
WebSocketSharp已经多年没有更新稳定版本,存在不少遗留bug。如果是正式项目,可以换成ASP.NET Core内置的WebSocket中间件,原生实现没有这类时序问题,维护性和稳定性都更好。
内容的提问来源于stack exchange,提问作者Kaisadilla
相关产品推荐
相关产品推荐

