You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WebSocket发送超过125字节字符串的问题求助

嗨,我太懂你被WebSocket大消息帧格式搞晕的感觉了——之前我也踩过好几个坑,尤其是客户端必须加掩码这个要求,稍不注意就触发协议错误。咱们一步步捋清楚,先把核心逻辑讲明白,再给你能直接用的代码示例。

先搞懂:大消息的两种发送思路

超过125字节的消息不一定非要拆成多帧,其实有两种实用方案:

  1. 单帧扩展长度发送:用一个帧承载全部内容,只是把长度字段换成扩展格式(126对应2字节长度,127对应8字节),适合你说的2000字符场景,简单高效
  2. 多帧拆分发送:把消息切成多个小帧,第一帧标记为“文本帧+未完成”,后续帧标记为“续帧”,最后一帧标记为“完成”,适合超大消息的场景

不管用哪种,客户端发送的帧必须加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:59:05