介绍开源的.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 ...
随机推荐
- django开发个人简易Blog——构建项目结构
开发之前第一步,就是构造整个的项目结构.这就好比作一幅画,第一步就是描绘轮廓,有了轮廓,剩下的就是慢慢的填充细节.项目结构规划如下图: 项目结构描述: 本项目以fengzhengBlog为根目录. a ...
- 网络异步编程(C#)团购课
新生命开发团队大石头讲解网络异步编程(C#) 内容:网络编程基础.IOCP.APM.SAEA 时长:2~3小时 价格:20元,20人及以上成团,http://item.taobao.com/item. ...
- ehcache2拾遗之cache持久化
问题描述 应用在使用过程中会需要重启等,但是如果ehcache随着应用一起重启,那么刚重启的时候就会出现大量的miss,需要一定的访问量来重建缓存,如果缓存能够持久化,重启之后可以复用将会有助于缓解重 ...
- java.rmi.NoSuchObjectException: no such object in table
jmx链接的时候,最简单的例子都行不通,郁闷,出现了: 参考:http://reiz6153.blog.163.com/blog/static/401089152009442723208/ 代码: M ...
- 用CSS实现居中的方式
直接放链接吧,最近大量时间放在看书上了,不想玩游戏,不想看电影,只想看书,早日做出自己的网站卖广告. CSS居中
- underscore源码阅读记录(二)
引自underscore.js context参数用法 _.each(list, iteratee, [context]); context为上下文,如果传递了context参数,则把iterator ...
- Bootstrap~多级导航(级联导航)的实现
回到目录 在bootstrap官方来说,导航最多就是两级,两级以上是无法实现的,大叔找了一些第三方的资料,终于找到一个不错的插件,使用上和效果上都还不错,现在和大家分享一下 插件地址:http://v ...
- hadoop安装遇到的各种异常及解决办法
hadoop安装遇到的各种异常及解决办法 异常一: 2014-03-13 11:10:23,665 INFO org.apache.hadoop.ipc.Client: Retrying connec ...
- Liferay7 BPM门户开发之43: Gradle依赖管理
进入liferay v7.0,官方推荐使用Gradle进行依赖管理和发布,所以必须知道Gradle的用法,网上资料很多,不赘述 只写依赖管理的分类 一般用外部仓库依赖,也可以用本地文件依赖(依赖本地j ...
- Archlinux 2015.07.01 和 Windows7 双系统 安装教程
提前在windows7下给Archlinux预留一个分区,大小最好在20G以上(根据自己硬盘情况分配). 第一步,安装前的准备 从arch官网下载最新的ISO文件archlinux-2015.07.0 ...