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

namespace NewLife.Net.Handlers;

/// <summary>
/// 按指定分割字节来处理粘包的处理器
/// </summary>
/// <remarks>
/// 默认以"0x0D 0x0A"即换行来分割,分割的包包含分割字节本身,使用时请注意。
/// 默认分割方式:ISocket.Add&lt;SplitDataCodec&gt;()
/// 自定义分割方式:ISocket.Add(new SplitDataHandler { SplitData = 自定义分割字节数组 })
/// 自定义最大缓存大小方式:ISocket.Add(new SplitDataHandler { MaxCacheDataLength = 2048 })
/// 自定义方式:ISocket.Add(new SplitDataHandler { MaxCacheDataLength = 2048, SplitData = 自定义分割字节数组 })
/// </remarks>
public class SplitDataCodec : Handler
{
    /// <summary>
    /// 粘包分割字节数据(默认0x0D,0x0A)
    /// </summary>
    public Byte[] SplitData { get; set; } = new Byte[] { 0x0D, 0x0A };

    /// <summary>
    /// 最大缓存待处理数据,默认1024字节
    /// </summary>
    public Int32 MaxCacheDataLength { get; set; } = 1024;

    /// <summary>读取数据</summary>
    /// <param name="context"></param>
    /// <param name="message"></param>
    /// <returns></returns>
    public override Object Read(IHandlerContext context, Object message)
    {
        if (message is not Packet pk) return base.Read(context, message);

        // 解码得到多个消息
        var list = Decode(context, pk);
        if (list == null) return null;

        foreach (var msg in list)
        {
            // 把数据发送给后续处理器
            //var rs = base.Read(context, msg);

            // 匹配输入回调,让上层事件收到分包信息
            //context.FireRead(rs);
            base.Read(context, msg);
        }

        return null;
    }

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

    #region 粘包处理
    /// <summary>解码</summary>
    /// <param name="context"></param>
    /// <param name="pk">包</param>
    /// <returns></returns>
    protected 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
            {
                MaxCache = MaxCacheDataLength,
                GetLength = GetLineLength,
                Tracer = (context.Owner as ISocket)?.Tracer
            };
        }

        return pc.Parse(pk);
    }

    /// <summary>
    /// 获取包含分割字节在内的数据长度
    /// </summary>
    /// <param name="pk"></param>
    /// <returns></returns>
    protected Int32 GetLineLength(Packet pk)
    {
        var idx = pk.IndexOf(SplitData);
        if (idx < 0) return 0;

        return idx + SplitData.Length;
    }
    #endregion
}