解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
3.49 KiB
X
using NewLife.Data;
using NewLife.Messaging;

namespace NewLife.Http;

/// <summary>WebSocket 分片重组器(RFC 6455 §5.4)。累积 FIN=0 数据帧,末片(FIN=1)合并为完整消息交付</summary>
/// <remarks>
/// 每个连接持有一个实例;控制帧(Close/Ping/Pong)不可分片,不经过本重组器。
/// 首片为文本或二进制帧(opcode=1/2,FIN=0),后续续片为附加数据帧(opcode=0)。
/// </remarks>
internal sealed class WebSocketFragment
{
    private readonly List<Byte[]> _fragments = [];
    private WebSocketMessageType _type;
    private Int32 _length;

    /// <summary>分片重组上限(字节)。超过丢弃当前分片序列,避免内存无界增长。默认 16M</summary>
    /// <remarks>超限后置位 <see cref="TooBig" />:调用方据此按 RFC 6455 §7.4.1 失败连接(1009 Message Too Big),
    /// 而非静默丢弃——静默丢弃会让对端以为消息已送达</remarks>
    public Int32 MaxFragments { get; set; } = 16 * 1024 * 1024;

    /// <summary>是否正在累积分片</summary>
    public Boolean Active => _fragments.Count > 0;

    /// <summary>是否因超过上限而丢弃了当前分片序列</summary>
    /// <remarks>下一次 <see cref="Begin"/> 开始新序列时复位;置位期间到达的续片按无首片的孤立续片忽略</remarks>
    public Boolean TooBig { get; private set; }

    /// <summary>开始累积新的分片序列。新首片到来时丢弃未完成的旧序列(协议错误或对端中断)</summary>
    /// <param name="type">首片类型(文本或二进制)</param>
    /// <param name="payload">首片负载(内部拷贝)</param>
    public void Begin(WebSocketMessageType type, IPacket? payload)
    {
        _fragments.Clear();
        _type = type;
        TooBig = false;

        // 首片同样受上限约束:不检查会让一个超大 FIN=0 首片直接分配并绕过上限(Append 侧已有同样检查)。
        // 先按长度判定再拷贝,避免为注定被丢弃的分片白拷贝一大块
        var total = payload?.Total ?? 0;
        if (total > MaxFragments)
        {
            TooBig = true;
            _length = 0;
            return;
        }

        var data = payload?.ToArray() ?? [];
        _fragments.Add(data);
        _length = data.Length;
    }

    /// <summary>追加续片;末片(FIN=1)时合并为完整消息</summary>
    /// <param name="fin">是否末片</param>
    /// <param name="payload">续片负载(内部拷贝)</param>
    /// <returns>合并后的完整消息;未到末片、超限或无首片的孤立续片返回 null</returns>
    public WsMessage? Append(Boolean fin, IPacket? payload)
    {
        if (_fragments.Count == 0) return null;

        var data = payload?.ToArray() ?? [];
        _length += data.Length;
        if (_length > MaxFragments)
        {
            // 超限:丢弃整段分片序列,并置标记由调用方失败连接(1009)
            TooBig = true;
            _fragments.Clear();
            return null;
        }

        _fragments.Add(data);
        if (!fin) return null;

        // 末片:合并为完整消息
        var buf = new Byte[_length];
        var offset = 0;
        foreach (var item in _fragments)
        {
            item.CopyTo(buf, offset);
            offset += item.Length;
        }
        _fragments.Clear();

        var msg = new WsMessage { Fin = true, Type = _type };
        msg.SetBody(new ArrayPacket(buf));

        return msg;
    }
}