Nico
Published on 2025-09-29 / 0 Visits
0
0

信息分片接受

#C#

二、需要自定义分隔的方案:二进制帧头 + 负载 当你希望在一条消息中承载多帧,或需要在多个消息中分片并重组,就要设计一个“帧头”(header),明确边界与元信息。
常见的自定义帧头字段:
magic/signature:固定字节标识,便于同步与校验,如 0x42 0x48 0x46 0x52 (“BHFR”)
version:协议版本
frameId:递增帧号(uint64)
timestamp:时间戳(uint64)
mimeLen:MIME 类型长度(如 image/jpeg)
payloadLen:本帧负载长度(uint32/uint64)
可选:CRC32、宽高、分片参数等
发送端将“帧头 + 负载”作为一个二进制消息发送;接收端按 DataView 读帧头,再按 payloadLen 读取负载。这也能实现可靠的“帧分隔”。
WPF 客户端解析示例:

private void ParseAndRenderFrames(ReadOnlySpan<byte> data)
{
    int offset = 0;
    while (offset + 24 <= data.Length) // 假设固定部分头 24 字节
    {
        // 读取 magic
        if (!(data[offset] == 0x42 && data[offset + 1] == 0x48 && data[offset + 2] == 0x46 && data[offset + 3] == 0x52))
        {
            // 找不到同步字节,尝试跳过或重置
            break;
        }

        offset += 4;
        byte version = data[offset]; offset += 1;
        // frameId (uint64 little-endian)
        ulong frameId = BitConverter.ToUInt64(data.Slice(offset, 8)); offset += 8;
        ulong ts = BitConverter.ToUInt64(data.Slice(offset, 8)); offset += 8;
        byte mimeLen = data[offset]; offset += 1;

        if (offset + mimeLen + 4 > data.Length) break;
        string mime = Encoding.ASCII.GetString(data.Slice(offset, mimeLen)); offset += mimeLen;

        uint payloadLen = BitConverter.ToUInt32(data.Slice(offset, 4)); offset += 4;

        if (offset + payloadLen > data.Length) break; // 不完整,等待下次拼接
        var payload = data.Slice(offset, (int)payloadLen).ToArray();
        offset += (int)payloadLen;

        // 解码该帧
        RenderImage(payload);
    }
}

注意:
如果一条 WS 消息内可能包含多帧,则在 ReceiveAsync 累积到 EndOfMessage 后,将整条消息送入 ParseAndRenderFrames 分解。
如果帧可能跨消息分片,就需要在 ReceiveLoop 里维持一个持续增长的缓冲,将每次收到的片段追加进去,只有当累计数据达到“完整帧”所需长度时才取出一帧,并把缓冲前移。可以用 MemoryStream 或环形缓冲。


Comment