C# ClientWebSocket接收二进制消息时抛出异常问题
结合你的场景(Windows客户端用System.Net.WebSockets,嵌入式设备用libwebsocket),以及你提到的“回环发送能成功接收,但设备发来的二进制帧触发协议格式错误”,这个问题大概率是WebSocket帧的掩码不符合规范导致的,再加上你的接收代码有个小bug需要修正,下面一步步说:
核心原因:libwebsocket发送的帧带了不必要的掩码
根据WebSocket RFC规范,只有客户端发送的帧需要添加掩码,服务端(你的嵌入式设备)发送的帧绝对不能带掩码。而System.Net.WebSockets客户端会严格校验这一点——如果收到服务端发来的带掩码的帧,直接就会抛出E_INVALID_PROTOCOL_FORMAT(0x83760002)异常,这就是你遇到的问题。
之前回环测试成功,是因为你自己客户端发的帧带掩码是符合规范的,客户端接收自己发的帧(作为echo)时,掩码校验是通过的。但设备作为服务端,要是被配置成发送带掩码的帧,就会触发客户端的协议校验错误。
解决设备端配置问题
检查libwebsocket的发送逻辑:
- 查找设备代码中调用
lws_write的地方,确认是否传入了LWS_WRITE_MASK标志。如果有,直接移除这个标志,服务端发送帧不需要加掩码。 - 如果你用的是libwebsocket的更高版本,也可以检查相关的上下文配置,确保没有强制服务端发送带掩码的帧的选项。
修复客户端接收代码的bug
你的接收循环里有个容易被忽略的问题:每次接收后直接把整个buffer数组添加到receivedBytes里,但ReceiveAsync返回的result.Count才是本次实际收到的有效字节数——buffer里剩下的字节都是未初始化的垃圾数据。虽然这个不是触发异常的直接原因,但会导致后续处理二进制数据时出现脏数据,必须修正:
原来的代码:
receivedBytes.AddRange(receiveBuffer.Array);
修改为:
receivedBytes.AddRange(receiveBuffer.Array.Take(result.Count));
额外排查步骤(如果上面的方法没解决)
如果调整设备端掩码后还是异常,可以用Wireshark抓WebSocket包,检查帧头的细节:
- 确认设备发送的二进制帧的opcode是0x2(对应二进制类型),不是其他错误的opcode。
- 检查帧的分段标记(FIN位)是否正确:如果是单帧消息,FIN位应该设为1;如果是分段消息,后续帧的opcode应该是0x0(延续帧)。
- 确认帧的负载长度字段是否正确,没有超过WebSocket规范的限制。
内容的提问来源于stack exchange,提问作者Guthni

