v11.0.2024.1101 改进IPacket
大石头 authored at 2024-11-01 16:11:26
3.30 KiB
X
using NewLife.Buffers;
using NewLife.Data;
using NewLife.Messaging;
using NewLife.Model;

namespace NewLife.Net.Handlers;

/// <summary>长度字段作为头部</summary>
public class LengthFieldCodec : MessageCodec<IPacket>
{
    #region 属性
    /// <summary>长度的偏移量,截取数据包时加上,否则将会漏掉长度之间的数据包,如MQTT</summary>
    public Int32 Offset { get; set; }

    /// <summary>长度占据字节数,1/2/4个字节,0表示压缩编码整数,默认2</summary>
    public Int32 Size { get; set; } = 2;

    /// <summary>过期时间,超过该时间后按废弃数据处理,默认5000ms</summary>
    public Int32 Expire { get; set; } = 5_000;
    #endregion

    /// <summary>编码</summary>
    /// <param name="context"></param>
    /// <param name="msg"></param>
    /// <returns></returns>
    protected override Object Encode(IHandlerContext context, IPacket msg)
    {
        var dlen = msg.Total;

        // 修正压缩编码
        var len = Math.Abs(Size);
        if (Size == 0) len = IOHelper.GetEncodedInt(dlen).Length;

        // 尝试退格,直接利用缓冲区
        var pk = msg.ExpandHeader(len);
        var writer = new SpanWriter(pk.GetSpan()) { IsLittleEndian = Size > 0 };

        switch (Size)
        {
            case 0:
                writer.WriteEncodedInt(dlen);
                break;
            case 1:
                writer.WriteByte((Byte)dlen);
                break;
            case 2:
                writer.Write((UInt16)dlen);
                break;
            case 4:
                writer.Write((UInt32)dlen);
                break;
            case -2:
                writer.Write((UInt16)dlen);
                break;
            case -4:
                writer.Write((UInt32)dlen);
                break;
            default:
                throw new NotSupportedException();
        }

        return pk;
    }

    /// <summary>解码</summary>
    /// <param name="context"></param>
    /// <param name="pk"></param>
    /// <returns></returns>
    protected override IList<IPacket>? Decode(IHandlerContext context, IPacket pk)
    {
        if (context.Owner is not IExtend ss) return null;

        if (ss["Codec"] is not PacketCodec pc)
        {
            ss["Codec"] = pc = new PacketCodec
            {
                Expire = Expire,
                GetLength = p => GetLength(p, Offset, Size),
                GetLength2 = p => GetLength(p, Offset, Size),
                //Offset = Offset,
                MaxCache = MaxCache,
                Tracer = (context.Owner as ISocket)?.Tracer
            };
        }

        var pks = pc.Parse(pk);

        // 跳过头部长度
        var len = Offset + Math.Abs(Size);
        for (var i = 0; i < pks.Count; i++)
        {
            pks[i] = pks[i].Slice(len, -1, true);
        }

        return pks;
    }

    /// <summary>连接关闭时,清空粘包编码器</summary>
    /// <param name="context"></param>
    /// <param name="reason"></param>
    /// <returns></returns>
    public override Boolean Close(IHandlerContext context, String reason)
    {
        if (context.Owner is IExtend ss) ss["Codec"] = null;

        return base.Close(context, reason);
    }
}