[feat]PacketCodec数据包编码器增加基于Span的长度解析。基于Span的性能更优,但是它不支持链式包。网络接收中几乎不存在链式包
石头 authored at 2024-10-13 15:44:14
3.62 KiB
X
using NewLife.Data;
using NewLife.Messaging;
using NewLife.Model;
using NewLife.Reflection;
using NewLife.Serialization;

namespace NewLife.Net.Handlers;

/// <summary>标准网络封包。头部4字节定长</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/srmp
/// </remarks>
public class StandardCodec : MessageCodec<IMessage>
{
    ///// <summary>编码器。用于编码非消息对象</summary>
    //public IPacketEncoder? Encoder { get; set; }

    private Int32 _gid;

    /// <summary>写入数据</summary>
    /// <param name="context"></param>
    /// <param name="message"></param>
    /// <returns></returns>
    public override Object? Write(IHandlerContext context, Object message)
    {
        // 基础类型优先编码
        if (message.GetType().IsBaseType())
        {
            message = new DefaultMessage { Flag = (Byte)DataKinds.String, Payload = (ArrayPacket)(message + "").GetBytes() };
        }
        else if (message is Byte[] buf)
        {
            message = new ArrayPacket(buf);
        }
        else if (message is IAccessor accessor)
        {
            message = accessor.ToPacket();
        }

        if (message is IPacket pk)
        {
            var dm = new DefaultMessage { Flag = (Byte)DataKinds.Packet, Payload = pk };
            message = dm;

            // 从上下文中获取标记位
            if (context is IExtend ext && ext["Flag"] is DataKinds dk)
                dm.Flag = (Byte)dk;
        }

        if (message is DefaultMessage msg && !msg.Reply && msg.Sequence == 0)
            msg.Sequence = (Byte)Interlocked.Increment(ref _gid);

        return base.Write(context, message);
    }

    /// <summary>加入队列</summary>
    /// <param name="context"></param>
    /// <param name="msg"></param>
    /// <returns></returns>
    protected override void AddToQueue(IHandlerContext context, IMessage msg)
    {
        if (!msg.Reply) base.AddToQueue(context, msg);
    }

    /// <summary>解码</summary>
    /// <param name="context"></param>
    /// <param name="pk"></param>
    /// <returns></returns>
    protected override IList<IMessage>? 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
            {
                GetLength = DefaultMessage.GetLength,
                GetLength2 = DefaultMessage.GetLength,
                MaxCache = MaxCache,
                Tracer = (context.Owner as ISocket)?.Tracer
            };
        }

        var pks = pc.Parse(pk);
        var list = new List<IMessage>();
        foreach (var item in pks)
        {
            var msg = new DefaultMessage();
            if (msg.Read(item)) list.Add(msg);
        }

        return list;
    }

    /// <summary>是否匹配响应</summary>
    /// <param name="request"></param>
    /// <param name="response"></param>
    /// <returns></returns>
    protected override Boolean IsMatch(Object? request, Object? response)
    {
        return request is DefaultMessage req &&
            response is DefaultMessage res &&
            req.Sequence == res.Sequence;
    }

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