指定粘包编码器的最大缓存数,避免遇到异常报文时内存暴涨
智能大石头 authored at 2024-01-10 02:20:25 大石头 committed at 2024-01-20 14:17:42
3.45 KiB
X
using NewLife.Data;
using NewLife.Messaging;
using NewLife.Model;

namespace NewLife.Net.Handlers;

/// <summary>长度字段作为头部</summary>
public class LengthFieldCodec : MessageCodec<Packet>
{
    #region 属性
    /// <summary>长度所在位置</summary>
    public Int32 Offset { get; set; }

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

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

    /// <summary>编码</summary>
    /// <param name="context"></param>
    /// <param name="msg"></param>
    /// <returns></returns>
    protected override Object Encode(IHandlerContext context, Packet msg)
    {
        var len = Math.Abs(Size);
        var buf = msg.Data;
        var idx = 0;
        var dlen = msg.Total;

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

        // 尝试退格,直接利用缓冲区
        if (msg.Offset >= len)
        {
            idx = msg.Offset - len;
            msg.Set(msg.Data, msg.Offset - len, msg.Count + len);
        }
        // 新建数据包,形成链式结构
        else
        {
            buf = new Byte[len];
            msg = new Packet(buf) { Next = msg };
        }

        switch (Size)
        {
            case 0:
                var buf2 = IOHelper.GetEncodedInt(dlen);
                buf.Write(idx, buf2);
                break;
            case 1:
                buf[idx] = (Byte)dlen;
                break;
            case 2:
                buf.Write((UInt16)dlen, idx);
                break;
            case 4:
                buf.Write((UInt32)dlen, idx);
                break;
            case -2:
                buf.Write((UInt16)dlen, idx, false);
                break;
            case -4:
                buf.Write((UInt32)dlen, idx, false);
                break;
            default:
                throw new NotSupportedException();
        }

        return msg;
    }

    /// <summary>解码</summary>
    /// <param name="context"></param>
    /// <param name="pk"></param>
    /// <returns></returns>
    protected override IList<Packet>? Decode(IHandlerContext context, Packet 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),
                Offset = Offset,
                MaxCache = MaxCache,
                Tracer = (context.Owner as ISocket)?.Tracer
            };
        }

        var pks = pc.Parse(pk);

        // 跳过头部长度
        var len = Offset + Math.Abs(Size);
        foreach (var item in pks)
        {
            item.Set(item.Data, item.Offset + len, item.Count - len);
        }

        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);
    }
}