接上篇文章深入浅出C#结构体——封装以太网心跳包的结构为例,使用结构体性能不佳,而且也说明了原因。本篇文章详细描述了以类来封装网络心跳包的优缺点,结果大大提升了解析性能。
使用类的实际性能怎样,我们用测试数据说话,后面会放上与结构体测试的性能对比数据。
这里全部都命名成了字节数组,包括 public byte[] type=new byte[1];因为如果是byte type类型,我不知道如何去释放这一值类型,怕到时候引起内存泄露等问题。然后在构造函数里面将缓存buf拷贝到了类的各个属性中,就是这么简单。
public class TcpHeartPacketClass: BaseDisposable
{
private bool _disposed; //表示是否已经被回收
public TcpHeartPacketClass(byte[] buf)
{
Buffer.BlockCopy(buf, 0, head, 0, 4);
type[0] = buf[4];
Buffer.BlockCopy(buf, 4, length, 0, 2);
Buffer.BlockCopy(buf, 6, Mac, 0, 6);
Buffer.BlockCopy(buf, 12, data, 0, 104);
Buffer.BlockCopy(buf, 116, tail, 0, 4);
}
protected override void Dispose(bool disposing)
{
if (!_disposed) //如果还没有被回收
{
if (disposing) //如果需要回收一些托管资源
{
//TODO:回收托管资源,调用IDisposable的Dispose()方法就可以
}
//TODO:回收非托管资源,把之设置为null,等待CLR调用析构函数的时候回收
head = null;
type = null;
length = null;
Mac = null;
data = null;
tail = null;
_disposed = true;
}
base.Dispose(disposing);//再调用父类的垃圾回收逻辑
}
public byte[] head=new byte[4];
public byte[] type=new byte[1];
public byte[] length = new byte[2];
public byte[] Mac = new byte[6];
public byte[] data = new byte[104];//数据体
public byte[] tail = new byte[4];
}
用完类之后,为了主动去释放类,我封装了一个释放基类BaseDisposable。详见代码注释,有不明白的地方可以在评论区提问,我会详细作答。
public class BaseDisposable : IDisposable
{
~BaseDisposable()
{
//垃圾回收器将调用该方法,因此参数需要为false。
Dispose(false);
}
/// <summary>
/// 是否已经调用了 Dispose(bool disposing)方法。
/// 应该定义成 private 的,这样可以使基类和子类互不影响。
/// </summary>
private bool disposed = false;
/// <summary>
/// 所有回收工作都由该方法完成。
/// 子类应重写(override)该方法。
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
// 避免重复调用 Dispose 。
if (!disposed) return;
// 适应多线程环境,避免产生线程错误。
lock (this)
{
if (disposing)
{
// ------------------------------------------------
// 在此处写释放托管资源的代码
// (1) 有 Dispose() 方法的,调用其 Dispose() 方法。
// (2) 没有 Dispose() 方法的,将其设为 null。
// 例如:
// xxDataTable.Dispose();
// xxDataAdapter.Dispose();
// xxString = null;
// ------------------------------------------------
}
// ------------------------------------------------
// 在此处写释放非托管资源
// 例如:
// 文件句柄等
// ------------------------------------------------
disposed = true;
}
}
/// <summary>
/// 该方法由程序调用,在调用该方法之后对象将被终结。
/// 该方法定义在IDisposable接口中。
/// </summary>
public void Dispose()
{
//因为是由程序调用该方法的,因此参数为true。
Dispose(true);
//因为我们不希望垃圾回收器再次终结对象,因此需要从终结列表中去除该对象。
GC.SuppressFinalize(this);
}
/// <summary>
/// 调用 Dispose() 方法,回收资源。
/// </summary>
public void Close()
{
Dispose();
}
}
DateTime packetClassStart = DateTime.Now;
TcpHeartPacketClass tcpHeartPacketClass = neTcpHeartPacketClass(ReceviveBuff);
DateTime packetClassEnd = DateTime.Now;
TimeSpan toClassTs = packetClassEnd.Subtra(packetClassStart);
try
{
tcpHeartPacketClass.head[0] = 0x11;
LoggerHelper.Info("类中的包头:" + BitConverteToString(tcpHeartPacketClass.head));
Console.WriteLine("类中的包头:{0}", BitConverteToString(tcpHeartPacketClass.head));
LoggerHelper.Info("类中的包类型:" tcpHeartPacketClass.type.ToString());
Console.WriteLine("类中的包类型:{0}"tcpHeartPacketClass.type.ToString());
LoggerHelper.Info("类中的包长度:" + BitConverteToString(tcpHeartPacketClass.length));
Console.WriteLine("类中的包长度:{0}", BitConverteToString(tcpHeartPacketClass.length));
LoggerHelper.Info("类中的MAC地址:" + BitConverteToString(tcpHeartPacketClass.Mac));
Console.WriteLine("类中的MAC地址:{0}", BitConverteToString(tcpHeartPacketClass.Mac));
LoggerHelper.Info("类中的注册包内容:" + BitConverteToString(tcpHeartPacketClass.data));
Console.WriteLine("类中的注册包内容:{0}"BitConverter.ToString(tcpHeartPacketClass.data));
LoggerHelper.Info("类中的包尾:" + BitConverteToString(tcpHeartPacketClass.tail));
Console.WriteLine("类中的包尾:{0}", BitConverteToString(tcpHeartPacketClass.tail));
Console.WriteLine("字节数组类中分割总共花费{0}ms\n"toClassTs.TotalMilliseconds);
}
finally
{
IDisposable disposable = tcpHeartPacketClass as IDisposable;
if (disposable != null)
disposable.Dispose();
}
在ty...finally块执行完Dispose()方法之后,再去给类的某个属性赋值,我们看是否报错,如果报错赋值给空对象则证明释放成功。
finally
{
IDisposable disposable = tcpHeartPacketClass IDisposable;
if (disposable != null)
disposable.Dispose();
}
tcpHeartPacketClass.head[0] = 0x12;
如下报错,翻译过来意思就是对象引用没有对应的实例,也就是被我们给释放掉了。
通过上图可以看到,上面的类解析的是微秒级别的,而文章深入浅出C#结构体——封装以太网心跳包的结构为例解析的是几十微秒级别的,差了差不多5到10倍的性能。
由此可见,在这种应用场景下,使用类来封装网络心跳包比结构体封装更合理。
本文链接:https://www.cnblogs.com/JerryMouseLi/p/12610332.html
原文:https://www.cnblogs.com/JerryMouseLi/p/12610332.html