C# WebSocket发送超过125字节字符串的问题求助
嗨,我太懂你被WebSocket大消息帧格式搞晕的感觉了——之前我也踩过好几个坑,尤其是客户端必须加掩码这个要求,稍不注意就触发协议错误。咱们一步步捋清楚,先把核心逻辑讲明白,再给你能直接用的代码示例。
先搞懂:大消息的两种发送思路
超过125字节的消息不一定非要拆成多帧,其实有两种实用方案:
- 单帧扩展长度发送:用一个帧承载全部内容,只是把长度字段换成扩展格式(126对应2字节长度,127对应8字节),适合你说的2000字符场景,简单高效
- 多帧拆分发送:把消息切成多个小帧,第一帧标记为“文本帧+未完成”,后续帧标记为“续帧”,最后一帧标记为“完成”,适合超大消息的场景
不管用哪种,客户端发送的帧必须加4字节掩码——这是新手最容易踩的坑,服务器收到无掩码的客户端帧会直接断开并报协议错误。
你遇到错误的大概率原因
你说的“WebSocket Protocol Error”和“Invalid frame hea...”,基本逃不出这几个问题:
- 客户端发送的帧没加掩码,或者掩码长度不对(必须是4字节)
- 把字符数当成了字节数计算长度(UTF8编码后一个字符可能占1-4字节,得用编码后的字节数组长度)
- 多帧的FIN位和OPCODE设置错误(续帧的OPCODE必须是0x0,最后一帧要把FIN位设为1)
- 长度字段的字节顺序搞反了(WebSocket要求大端序,C#默认是小端,得手动反转)
代码示例:C#实现两种发送方式
方式一:单帧发送大文本(推荐,适合你的场景)
2000字符用这个方法完全足够,不用拆帧,逻辑更简单:
using System; using System.Net.WebSockets; using System.Text; using System.Threading; public async Task SendLargeSingleFrameMessage(ClientWebSocket ws, string message) { // 把字符串转成UTF8字节数组,这才是真实的payload长度 byte[] payload = Encoding.UTF8.GetBytes(message); int payloadLength = payload.Length; var frameStream = new System.IO.MemoryStream(); // 第一个字节:FIN=1(单帧完成),OPCODE=0x1(文本类型) frameStream.WriteByte(0x81); // 第二个字节:MASK=1(客户端必须加),然后是长度标识 if (payloadLength <= 125) { frameStream.WriteByte((byte)(0x80 | payloadLength)); } else if (payloadLength <= ushort.MaxValue) { frameStream.WriteByte(0x80 | 126); // 2字节长度要转成大端序 byte[] lengthBytes = BitConverter.GetBytes((ushort)payloadLength); if (BitConverter.IsLittleEndian) Array.Reverse(lengthBytes); frameStream.Write(lengthBytes, 0, 2); } else { frameStream.WriteByte(0x80 | 127); // 8字节长度转大端序 byte[] lengthBytes = BitConverter.GetBytes((ulong)payloadLength); if (BitConverter.IsLittleEndian) Array.Reverse(lengthBytes); frameStream.Write(lengthBytes, 0, 8); } // 生成4字节随机掩码 byte[] mask = new byte[4]; new Random().NextBytes(mask); frameStream.Write(mask, 0, 4); // 对payload应用掩码(客户端必须做这一步) for (int i = 0; i < payload.Length; i++) { payload[i] = (byte)(payload[i] ^ mask[i % 4]); } // 写入掩码后的payload frameStream.Write(payload, 0, payload.Length); // 发送整个帧 await ws.SendAsync( new ArraySegment<byte>(frameStream.ToArray()), WebSocketMessageType.Text, true, CancellationToken.None); }
方式二:多帧拆分发送超大消息
如果你的消息未来会变得特别大,超过单帧承载上限,可以用这个拆分方法:
using System; using System.Net.WebSockets; using System.Text; using System.Threading; public async Task SendMultiFrameMessage(ClientWebSocket ws, string message, int frameChunkSize = 1024) { byte[] fullPayload = Encoding.UTF8.GetBytes(message); int totalLength = fullPayload.Length; int currentOffset = 0; while (currentOffset < totalLength) { // 计算当前帧要发送的字节数 int sendLength = Math.Min(frameChunkSize, totalLength - currentOffset); byte[] framePayload = new byte[sendLength]; Array.Copy(fullPayload, currentOffset, framePayload, 0, sendLength); var frameStream = new System.IO.MemoryStream(); // 第一个字节:FIN位(最后一帧设1,其余设0)+ OPCODE(第一帧0x1,后续续帧0x0) byte firstByte = (currentOffset == 0) ? (byte)0x01 : (byte)0x00; if (currentOffset + sendLength == totalLength) { firstByte |= 0x80; // 标记为最后一帧 } frameStream.WriteByte(firstByte); // 第二个字节:MASK=1 + 长度标识 if (sendLength <= 125) { frameStream.WriteByte((byte)(0x80 | sendLength)); } else if (sendLength <= ushort.MaxValue) { frameStream.WriteByte(0x80 | 126); byte[] lengthBytes = BitConverter.GetBytes((ushort)sendLength); if (BitConverter.IsLittleEndian) Array.Reverse(lengthBytes); frameStream.Write(lengthBytes, 0, 2); } else { frameStream.WriteByte(0x80 | 127); byte[] lengthBytes = BitConverter.GetBytes((ulong)sendLength); if (BitConverter.IsLittleEndian) Array.Reverse(lengthBytes); frameStream.Write(lengthBytes, 0, 8); } // 生成掩码并写入 byte[] mask = new byte[4]; new Random().NextBytes(mask); frameStream.Write(mask, 0, 4); // 掩码处理当前帧的payload for (int i = 0; i < framePayload.Length; i++) { framePayload[i] = (byte)(framePayload[i] ^ mask[i % 4]); } frameStream.Write(framePayload, 0, framePayload.Length); // 发送当前帧:最后一帧标记endOfMessage为true,其余为false await ws.SendAsync( new ArraySegment<byte>(frameStream.ToArray()), WebSocketMessageType.Text, currentOffset + sendLength == totalLength, CancellationToken.None); currentOffset += sendLength; // 可选:加个小延迟,避免给服务器造成瞬时压力 await Task.Delay(10); } }
最后再划几个重点
- 字节数≠字符数:一定要用
Encoding.UTF8.GetBytes(message).Length作为payload长度,不能直接用message.Length - 掩码不能忘:客户端发送的所有帧都必须带4字节掩码,否则服务器直接报错
- 大端序要注意:126/127对应的长度字段必须是大端字节序,C#默认小端,所以要反转数组
- 多帧的OPCODE规则:第一帧是0x1(文本),后续续帧必须是0x0,最后一帧FIN设为1
你可以先试试单帧的方法,应该就能解决你的问题了。
内容的提问来源于stack exchange,提问作者Luicy Willowfield
相关产品推荐
相关产品推荐

