using System;
using System.Collections.Generic;
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)
{
var ss = context.Owner as IExtend;
var pc = ss["Codec"] as PacketCodec;
if (pc == null) ss["Codec"] = pc = new PacketCodec { Expire = Expire, GetLength = p => GetLength(p, Offset, Size) };
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);
}
}
}
|