接上篇文章深入浅出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
如对本文有疑问, 点击进行留言回复!!
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
C#实现获取本地内网(局域网)和外网(公网)IP地址的方法分析
网友评论