介绍开源的.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 ...
随机推荐
- [FPGA] 2、新建并运行一个工程
上一篇将开发板的情况大致介绍了一下,这次将一步一步展示如何新建.调试并下载运行一个点亮LED的工程. 1)打开Quartus新建工程: 2)填写规则大致如下: 3)选择我们芯片的类型: 4)点击fil ...
- svn 忽略文件不管用
svn 不能对已添加过版本控制的文件进行忽略.于是乎,你会发先,你怎么忽略都不起作用.于是乎,该怎么办? svn忽略已添加到版本库文件或文件夹步骤: 将要忽略的文件或文件夹剪切到非工作拷贝目录. 在父 ...
- React Native02-开始运行 Android篇
1. 开始运行 1)用命令进入到新建的文件目录下,比如HelloWorld,再输入 react-native start: 在等待一段时间后,我们看到最后面有个地址,说明已经运行成功了. 我们输入地址 ...
- Redis中统计各种数据大小的方法
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/125.html?1455853369如果 MySQL 数据库比较大的话,我 ...
- 中小公司PMO不一样期间的责任
中小公司,又称中小型公司或中小企,它是与所在行业的大公司对比在人员规划.财物规划与运营规划上都对比小的经济单位.此类公司一般可由单自个或少数人供给资金构成,其招聘人数与营业额皆不大,因此在运营上多半是 ...
- Redis学习笔记~StackExchange.Redis实现分布式Session
回到目录 对于多WEB的环境现在已经是必须的了,很难想像一台WEB服务器面对百万并发的响应,所以,我们需要多台WEB服务器集群合作,来缓解这种高并发,高吞吐的场景,而对于多WEB的场景又会有个问题出现 ...
- ajax图片上传及FastDFS入门案例.
今天来开始写图片上传的功能, 现在的图片上传都讲求 上传完成后立刻回显且页面不刷新, 这里到底是怎么做的呢? 当然是借助于ajax了, 但是ajax又不能提交表单, 这里我们还要借助一个插件: jqu ...
- Atitit 多继承实现解决方案 java c#
Atitit 多继承实现解决方案 java c# Java c#都没有提供多继承的解决方案..默认从语言级别以及没办法多继承了. 只可以崽类库的级别实现拉.. 继承的原理就是,使用一个内部super指 ...
- Atitit prj 项目管理与行政管理(1)------项目环境的概览与建立
Atitit prj 项目管理与行政管理(1)------项目环境的概览与建立 1. 环境的4大特点 (1)多样性与复杂性. (2)差异性.(3)变异性.(4)关联性.2 2. 环境的分类,最常用使用 ...
- Linux运维之道(大量经典案例、问题分析,运维案头书,红帽推荐)
Linux运维之道(大量经典案例.问题分析,运维案头书,红帽推荐) 丁明一 编 ISBN 978-7-121-21877-4 2014年1月出版 定价:69.00元 448页 16开 编辑推荐 1 ...