介绍开源的.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 ...
随机推荐
- jQuery为哪般去掉了浏览器检测
由于做HTML5相关的项目,许多前卫时髦的前端技术就需要考虑一下IE是否支持.要是在以前,可以很方便地调用jQuery的jQuery.browser来实现. If(jQuery.browser.msi ...
- git rm–r folder fatal:pathspec "" did not match any files
问题描述: 某年某月某日,在查看git库的时候,发现文件的分布和文件夹的名字是极其不合理的,所以移动和重命名了某些文件. 在删除(git rm –r folder)一个空文件夹的时候,出现错误:fat ...
- [翻译]AKKA笔记 - ACTOR MESSAGING - REQUEST AND RESPONSE -3
上次我们看Actor消息机制,我们看到开火-忘记型消息发出(意思是我们只要发个消息给Actor但是不期望有响应). 技术上来讲, 我们发消息给Actors就是要它的副作用. 这就是这么设计的.除了不响 ...
- 说说设计模式~桥梁模式(Bridge)
返回目录 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度? ...
- 基础才是重中之重~方法override详解
回到 目录 之所以写这篇文章,完全是因为这次代码审核,这次代码审核过程当中,出现了很多我认为基础知识不够扎实的问题,所以,打算把它们记录下来,共大家分享. 方法的override,即方法的覆写或者重写 ...
- EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~再续~添加对各只读服务器的心跳检测
回到目录 上一讲中基本实现了对数据库的读写分离,而在选择只读数据库上只是随机选择,并没有去检测数据库服务器是否有效,如服务器挂了,SQL服务停了,端口被封了等等,而本讲主要对以上功能进行一个实现,并对 ...
- 手打的笔记,java语法中的输入输出,语句,及注释。
手打的笔记: () 内的则为注意事项或者提示 public static void main (String[] args) ******(用一个方法)****{ int i = 10; int j ...
- Atitit 发帖机实现(3 )---usrQBN023 js提交ajax内容到后端规范与标准化
Atitit 发帖机实现(3 )---usrQBN023 js提交ajax内容到后端规范与标准化 大段内容务必要替换转义换行符号1 提交务必使用utf编码,否则解码后的可能缺失,是web serv ...
- iptables基础命令详解
TCP/IP基本概念: TCP/IP将网络分为四层:应用层,传输层,网络层,链路层. 传输层:定义了两种通信协议,分别为TCP协议和UDP协议. TCP协议:TCP协议在传输 数据过程中会检查数据的完 ...
- javascript中this指向
在简单函数中,this是指向当前对象,可用来获取当前对象某个属性,但随着函数变复杂,this很多情况不指向当前对象,而是指向window. 1.在独立调用函数中,具有全局执行环境,this指向wind ...