介绍开源的.net通信框架NetworkComms框架 源码分析(十一)PacketBuilder
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
数据包创建器 通信框架接收到的二进制数据存储到数据包创建器中 如果创建器中的数据足够多 即能够合成一个数据包 则从创建器中提取出去解析出数据包
namespace NetworkCommsDotNet.Tools { /// <summary> /// Packet data is generally broken into multiple variable sized byte chunks or 'partial packets'. /// This class provides features to effortlessly rebuild whole packets. /// 数据包创建器 通信框架接收到的二进制数据存储到数据包创建器中 如果创建器中的数据足够多 即能够合成一个数据包 则从创建器中提取出去解析出数据包 /// 解析完成后 创建器中相应的二进制数据会删除 /// 创建器好比一个流动的容器 /// </summary> public class PacketBuilder { List<byte[]> packets = new List<byte[]>(); List<int> packetActualBytes = new List<int>(); /// <summary> /// Locker object used for performing thread safe operations over this packet builder /// 同步锁 /// </summary> public object Locker { get; private set; } ; ; /// <summary> /// Create a new instance of the ConnectionPacketBuilder class /// 构造函数 /// </summary> public PacketBuilder() { Locker = new object(); } /// <summary> /// The total number of cached bytes. This is the sum of all bytes across all cached partial packets. See <see cref="TotalPartialPacketCount"/>. /// 已经收到的数据大小 /// </summary> public int TotalBytesCached { get { return totalBytesCached; } } /// <summary> /// The total number of cached partial packets. This is different from <see cref="TotalBytesCached"/> because each partial packet may contain a variable number of bytes. /// 数据包创建器中收到了多少个字节数组 /// </summary> public int TotalPartialPacketCount { get { lock (Locker) return packets.Count; } } /// <summary> /// The total number of bytes required to rebuild the next whole packet. /// 当期期望收到的数(即当前接收的数据包的大小) 如果PacketBuilder中的数据足够一个数据包的大小 则进行下一步解析 /// </summary> public int TotalBytesExpected { get { lock (Locker) return totalBytesExpected; } set { lock (Locker) totalBytesExpected = value; } } /// <summary> /// Clear N bytes from cache, starting with oldest bytes first. /// 从缓存中删除N个字节 这个功能一般是解析完成当前数据包后,删除相应的二进制数据 /// </summary> /// <param name="numBytesToRemove">The total number of bytes to be removed.</param> public void ClearNTopBytes(int numBytesToRemove) { lock (Locker) { ) { if (numBytesToRemove > totalBytesCached) throw new CommunicationException("Attempting to remove " + numBytesToRemove.ToString() + " bytes when ConnectionPacketBuilder only contains " + totalBytesCached.ToString()); ; //We will always remove bytes in order of the entries //按次序删除 ; i < packets.Count; i++) { if (packetActualBytes[i] > numBytesToRemove - bytesRemoved) { //Remove the necessary bytes from this packet and rebuild //New array length is the original length minus the amount we need to remove //从字节数据组中删除相应的数据 比如删除数组的前一部分 保留后一部分 需要对数组进行重建 byte[] newPacketByteArray = new byte[packetActualBytes[i] - (numBytesToRemove - bytesRemoved)]; Buffer.BlockCopy(packets[i], numBytesToRemove - bytesRemoved, newPacketByteArray, , newPacketByteArray.Length); bytesRemoved += packetActualBytes[i] - newPacketByteArray.Length; packets[i] = newPacketByteArray; packetActualBytes[i] = newPacketByteArray.Length; //Stop removing data here //停止删除 break; } ) { //When i == (packet.Count - 1) I would expect the above if condition to always be true throw new CommunicationException("This should be impossible."); } else { //If we want to remove this entire packet we can just set the list reference to null //如果删除当前数组的所有数据 只要把数组指向null即可 bytesRemoved += packetActualBytes[i]; packets[i] = null; packetActualBytes[i] = -; } } if (bytesRemoved != numBytesToRemove) throw new CommunicationException("bytesRemoved should really equal the requested numBytesToRemove"); //Reset the totalBytesRead //从已经缓存数中减去此次删除的数 totalBytesCached -= bytesRemoved; ) { //Get rid of any null packets //清除空的字节数组 List<byte[]> newPackets = new List<byte[]>(packets.Count); ; i < packets.Count; i++) { if (packets[i] != null) newPackets.Add(packets[i]); } packets = newPackets; //Remove any -1 entries //清除值为-1的记数 List<int> newPacketActualBytes = new List<int>(packetActualBytes.Count); ; i < packetActualBytes.Count; i++) { ) newPacketActualBytes.Add(packetActualBytes[i]); } packetActualBytes = newPacketActualBytes; } else { //This is faster if we have removed everything //如果我们删除了所有数据 可以如下操作 packets = new List<byte[]>(); packetActualBytes = new List<int>(); } //This is a really bad place to put a garbage collection as it hammers the CPU //GC.Collect(); //不应启用垃圾收集 } if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... removed " + numBytesToRemove + " bytes from packetBuilder."); } } /// <summary> /// Add a partial packet to the end of the cache by reference. /// 添加数据到相应的数据组 /// </summary> /// <param name="packetBytes">字节数量 The number of valid bytes in the provided partial packet</param> /// <param name="partialPacket">相关字节 A buffer which may or may not be full with valid bytes</param> public void AddPartialPacket(int packetBytes, byte[] partialPacket) { if (packetBytes > partialPacket.Length) throw new ArgumentException("packetBytes cannot be greater than the length of the provided partialPacket data."); ) throw new ArgumentException("packetBytes cannot be negative."); lock (Locker) { totalBytesCached += packetBytes; packets.Add(partialPacket); packetActualBytes.Add(packetBytes); if (NetworkComms.LoggingEnabled) { && totalBytesCached > ( * * )) NetworkComms.Logger.Warn("Packet builder cache contains " + (totalBytesCached / 1024.0).ToString("0.0") + "KB when 0KB are currently expected."); && totalBytesCached > totalBytesExpected * ) NetworkComms.Logger.Warn("Packet builder cache contains " + (totalBytesCached / 1024.0).ToString("0.0") + "KB when only " + (TotalBytesExpected / 1024.0).ToString("0.0") + "KB were expected."); } if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... added " + packetBytes + " bytes to packetBuilder."); } } /// <summary> /// Returns the most recently cached partial packet and removes it from the cache. /// Used to more efficiently utilise allocated memory space. /// 返回最后一个字节数组中的数据 /// 并从创建器中删除相应的数组和记数 /// </summary> /// <param name="lastPacketBytesRead">The number of valid bytes in the last partial packet added</param> /// <returns>A byte[] corresponding with the last added partial packet</returns> public byte[] RemoveMostRecentPartialPacket(ref int lastPacketBytesRead) { lock (Locker) { ) { ; lastPacketBytesRead = packetActualBytes[lastPacketIndex]; byte[] returnArray = packets[lastPacketIndex]; totalBytesCached -= packetActualBytes[lastPacketIndex]; packets.RemoveAt(lastPacketIndex); packetActualBytes.RemoveAt(lastPacketIndex); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... reusing byte[" + returnArray.Length + "] from packetBuilder which contains " + lastPacketBytesRead + " existing bytes."); return returnArray; } else throw new Exception("Unable to remove most recent packet as packet list is empty."); } } /// <summary> /// Returns the number of unused bytes in the most recently cached partial packet. /// 返回在最近的缓存部分包未使用的字节数。 /// </summary> /// <returns>The number of unused bytes in the most recently cached partial packet.</returns> public int NumUnusedBytesMostRecentPartialPacket() { lock (Locker) { ) { ; return packets[lastPacketIndex].Length - packetActualBytes[lastPacketIndex]; } else throw new Exception("Unable to return requested size as packet list is empty."); } } /// <summary> /// Returns the value of the first cached byte. /// 返回第一个缓存的字节的值。 /// </summary> /// <returns>The value of the first cached byte.</returns> public byte FirstByte() { lock (Locker) ][]; } /// <summary> /// Copies all cached bytes into a single array and returns. Original data is left unchanged. /// 把所有数据复制到一维数组中并返回 原始数据保持不变。 /// </summary> /// <returns>All cached data as a single byte[]</returns> public byte[] GetAllData() { lock (Locker) { byte[] returnArray = new byte[totalBytesCached]; ; ; i < packets.Count; i++) { Buffer.BlockCopy(packets[i], , returnArray, currentStart, packetActualBytes[i]); currentStart += packetActualBytes[i]; } return returnArray; } } /// <summary> /// Copies the requested cached bytes into a single array and returns. Original data is left unchanged. /// 根据参数 返回请求的数据 原始数据不变 /// </summary> /// <param name="startIndex">开始位置 The inclusive byte index to use as the starting position.</param> /// <param name="length">长度 The total number of desired bytes.</param> /// <returns>The requested bytes as a single array.</returns> public MemoryStream ReadDataSection(int startIndex, int length) { lock (Locker) { byte[] returnArray = new byte[length]; , writeTotal = ; int startingPacketIndex; ; //First find the correct starting packet //找到正确的字节数组的相应位置 ; startingPacketIndex < packets.Count; startingPacketIndex++) { if (startIndex - runningTotal <= packetActualBytes[startingPacketIndex]) { firstPacketStartIndex = startIndex - runningTotal; break; } else runningTotal += packetActualBytes[startingPacketIndex]; } //Copy the bytes of interest //复制感兴趣的字节数据 for (int i = startingPacketIndex; i < packets.Count; i++) { if (i == startingPacketIndex) { if (length > packetActualBytes[i] - firstPacketStartIndex) //If we want from some starting point to the end of the packet 从字节数组的某个位置开始 Buffer.BlockCopy(packets[i], firstPacketStartIndex, returnArray, writeTotal, packetActualBytes[i] - firstPacketStartIndex); else { //We only want part of the packet 整个字节数组 Buffer.BlockCopy(packets[i], firstPacketStartIndex, returnArray, writeTotal, length); writeTotal += length; break; } writeTotal = packetActualBytes[i] - firstPacketStartIndex; } else { //We are no longer on the first packet 不在第一个数据包上 if (packetActualBytes[i] + writeTotal >= length) { //We have reached the last packet of interest 已经到达我们希望到达的最后一个数据包 Buffer.BlockCopy(packets[i], , returnArray, writeTotal, length - writeTotal); writeTotal += length - writeTotal; break; } else { Buffer.BlockCopy(packets[i], , returnArray, writeTotal, packetActualBytes[i]); writeTotal += packetActualBytes[i]; } } } if (writeTotal != length) throw new Exception("Not enough data available in packetBuilder to complete request. Requested " + length.ToString() + " bytes but only " + writeTotal.ToString() + " bytes were copied."); #if NETFX_CORE , returnArray.Length, false); #else , returnArray.Length, false, true); #endif } } } }
介绍开源的.net通信框架NetworkComms框架 源码分析(十一)PacketBuilder的更多相关文章
- DotNetty网络通信框架学习之源码分析
DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...
- 深入理解分布式调度框架TBSchedule及源码分析
简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...
- 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)
1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...
- 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...
- $Django cbv源码分析 djangorestframework框架之APIView源码分析
1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...
- MyBatis框架的使用及源码分析(十一) StatementHandler
我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...
- ④NuPlayer播放框架之Renderer源码分析
[时间:2016-11] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架,渲染器,render] 0 导读 之前我们分析了NuPlayer的实现代码,本文将重点聚 ...
- ⑤NuPlayer播放框架之GenericSource源码分析
[时间:2017-01] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架,GenericSource] 0 导读 GenericSource是NuPlayer:: ...
- ③NuPlayer播放框架之类NuPlayer源码分析
[时间:2016-10] [状态:Open] [关键词:android,nuplayer,开源播放器,播放框架] 0 引言 差不多一个月了,继续分析AOSP的播放框架的源码.这次我们需要深入分析的是N ...
- Laravel开发:Laravel框架门面Facade源码分析
前言 这篇文章我们开始讲 laravel 框架中的门面 Facade,什么是门面呢?官方文档: Facades(读音:/fəˈsäd/ )为应用程序的服务容器中可用的类提供了一个「静态」接口.Lara ...
随机推荐
- JavaScript 误区
接触JavaScript两年多遇到过各种错误,其中有一些让人防不胜防,原来对JavaScript的误会如此之深,仅以此文总结一下常见的各种想当然的误区 String replace string的re ...
- CocoaPod问题
CocoaPod问题 升级10.11后使用CocoaPod出现-bash: pod: command not found 解决办法 字数91 阅读10946 评论24 喜欢27 升级10.11后,运行 ...
- jQuery插件开发的五种形态[转]
这篇文章主要介绍了jQuery插件开发的五种形态小结,具体的内容就是解决javascript插件的8种特征,非常的详细. 关于jQuery插件的开发自己也做了少许研究,自己也写过多个插件,在自己的团队 ...
- Atitit截屏功能的设计解决方案
Atitit截屏功能的设计解决方案 自己实现.... 使用快捷键.. 弹出自己的win,,背景是屏幕快照 点击鼠标光标变成十字状态 出现截屏窗口调整截屏窗口位置与大小 释放鼠标,三个btn,, 复制 ...
- salesforce 零基础开发入门学习(十二)with sharing 、without sharing 、无声明区别
在salesforce中,声明类大概可以分成三类:分别是可以声明为with sharing,without sharing,以及两者均不声明. public with sharing class A ...
- contentEditable属性设置是否可编辑元素的内容
在HTML5中在标签新添加了一个属性contentEditable可以设置标签内的内容是否可以编辑: 设置contenteditable="true"标签内的元素(内容)可以编辑 ...
- python+selenium运行报错UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
使用python+selenium运行自动化脚本时,打印某一段文字出现UnicodeEncodeError: 'ascii' codec can't encode characters in posi ...
- 那些年我们写过的T-SQL(下篇)
下篇的内容很多都会在工作中用到,尤其是可编程对象,那些年我们写过的存储过程,有木有?到目前为止很多大型传统企业仍然很依赖存储过程.这部分主要难理解的部分是事务和锁机制这块,本文会进行简单的阐述.虽然很 ...
- javaweb+SSH实现简单的权限管理系统
权限管理,平时里很多地方我们都可以看到,比如聊QQ时群里的群主.管理员以及成员之间的功能是不一样的--大家一定会遇到的一个问题,所以整理 一下自己写权限系统的一些经验给大家,只起参考作用,也望大家笑纳 ...
- linux-redis
1.下载 6.启动 ./redis-server ../conf/redis.conf 7.测试 ./redis-cli -p 7030 set str "hello" ./red ...