介绍开源的.net通信框架NetworkComms框架 源码分析(四)Packet
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
先来看一下数据包的接口类
/// <summary>
/// Interface for defining Application Layer Protocol packets
/// 数据包接口
/// </summary>
public interface IPacket
{
/// <summary>
/// The packet header for this packet
/// 数据包包头
/// </summary>
PacketHeader PacketHeader { get; }
/// <summary>
/// The payload data stream
/// 数据包的数据部分
/// </summary>
StreamTools.StreamSendWrapper PacketData { get; }
/// <summary>
/// Returns the serialised bytes of the packet header appended by the serialised header size. This is required to
/// rebuild the header on receive.
/// 返回数据包包头被序列化后生成的二进制数据 这部分数据在被对方接收后将会用来重建数据包包头
/// </summary>
/// <returns>The serialised header as byte[]</returns>
byte[] SerialiseHeader(SendReceiveOptions options);
/// <summary>
/// Dispose of internal packet resources
/// 释放资源
/// </summary>
void Dispose();
}
/// <summary>
/// Wrapper for <see cref="PacketHeader"/> and packetData.
/// 数据包
/// </summary>
public class Packet : IDisposable, IPacket, IExplicitlySerialize
{
/// <summary>
/// If we serialise a whole packet we include the packet header
/// 数据包包头
/// </summary>
PacketHeader _packetHeader;
/// <summary>
/// And the payload object as byte[]. We cannot use type T here because we do not know the type of T
/// on deserialisation until we have the nested packet header.
/// 数据部分对应的二进制字节数组 我们不能使用T类型 因为我们在反序列化时不知道T的类型
/// </summary>
internal byte[] _payloadObjectBytes;
internal int _payloadSize;
StreamTools.StreamSendWrapper payloadStream;
/// <summary>
/// Parameterless constructor for deserialisation
/// 反序列化时使用的无参数构造函数
/// </summary>
private Packet()
{
}
/// <summary>
/// Create a new packet
/// 创建一个数据包
/// </summary>
/// <param name="sendingPacketTypeStr">发送的消息类型 The sending packet type</param>
/// <param name="payloadObject">发送的对象 The object to be sent</param>
/// <param name="options">收发参数 The <see cref="SendReceiveOptions"/> to be used to create this packet</param>
public Packet(string sendingPacketTypeStr, object payloadObject, SendReceiveOptions options)
{
Constructor(sendingPacketTypeStr, null, payloadObject, options, false);
}
/// <summary>
/// Create a new packet
/// 创建一个数据包
/// </summary>
/// <param name="sendingPacketTypeStr">数据包的消息类型 The sending packet type</param>
/// <param name="requestReturnPacketTypeStr">期待对方处理完成后返回的消息类型 The expected return packet type</param>
/// <param name="payloadObject">发送的对象 The object to be sent</param>
/// <param name="options">收发参数 The <see cref="SendReceiveOptions"/> to be used to create this packet</param>
public Packet(string sendingPacketTypeStr, string requestReturnPacketTypeStr, object payloadObject, SendReceiveOptions options)
{
Constructor(sendingPacketTypeStr, requestReturnPacketTypeStr, payloadObject, options, false);
}
/// <summary>
/// Private constructor used for nesting packets
/// 私有的构造函数 用于发送嵌套数据包
/// </summary>
/// <param name="sendingPacketTypeStr"></param>
/// <param name="requestReturnPacketTypeStr"></param>
/// <param name="payloadObject"></param>
/// <param name="options"></param>
/// <param name="isNested"></param>
private Packet(string sendingPacketTypeStr, string requestReturnPacketTypeStr, object payloadObject, SendReceiveOptions options, bool isNested)
{
Constructor(sendingPacketTypeStr, requestReturnPacketTypeStr, payloadObject, options, isNested);
}
private void Constructor<payloadObjectType>(string sendingPacketTypeStr, string requestReturnPacketTypeStr, payloadObjectType payloadObject, SendReceiveOptions options, bool isNested)
{
if (sendingPacketTypeStr == null || sendingPacketTypeStr == "") throw new ArgumentNullException("sendingPacketTypeStr", "The provided string can not be null or zero length.");
if (options == null) throw new ArgumentNullException("options", "The provided SendReceiveOptions cannot be null.");
if (options.DataSerializer == null) throw new ArgumentNullException("options", "The provided SendReceiveOptions.DataSerializer cannot be null. Consider using NullSerializer instead.");
//Check for security critical data processors 检查安全关键数据处理器
//There may be performance issues here 可能会有一点性能问题
bool containsSecurityCritialDataProcessors = false;
if (!options.Options.ContainsKey("UseNestedPacketType") && //We only need to perform this check if we are not already using a nested packet 我们只在没有使用嵌套数据包时执行检测
!isNested) //We do not perform this check within a nested packet 在嵌套数据包中我们不执行检测
{
foreach (DataProcessor processor in options.DataProcessors)
{
if (processor.IsSecurityCritical)
{
containsSecurityCritialDataProcessors = true;
break;
}
}
}
//By default the object to serialise will be the payloadObject
//默认 序列化的数据对象为 objectToSerialise
object objectToSerialise = payloadObject;
bool objectToSerialiseIsNull = false;
//We only replace the null with an empty stream if this is either in the nested packet
//or we will not be nesting
//如果要发送的数据包内容为Null 并且当前数据包为嵌套数据包 或者选项中不包含"使用嵌套数据包类型"的数据包中,我们创建空数据流(emptyStream)代替NULL对象
if (objectToSerialise == null &&
((!options.Options.ContainsKey("UseNestedPacketType") &&
!containsSecurityCritialDataProcessors) || isNested))
{
#if NETFX_CORE
], , , false);
#else
], , , false, true);
#endif
//If the sending object is null we set objectToSerialiseIsNull and create a zero length StreamSendWrapper
//The zero length StreamSendWrapper can then be passed to any data serializers
//如果要发送的数据包内容为NULL,我们设置 objectToSerialiseIsNull属性为True.并且创建一个长度为0的StreamSendWrapper对象
//这个长度为0的StreamSendWrapper对象可以传递给任何数据序列化器
objectToSerialiseIsNull = true;
objectToSerialise = new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(emptyStream, true));
}
//If we need to nest this packet
//如果我们需要嵌套数据包
//嵌套数据包概念:
//我的理解,把数据包中的数据部分包装成一个数据包
//普通数据包 对方反序列化后得到实际的数据类型
//嵌套数据包 对方反序列化得到Packet数据类型的数据 还要继续做解析
if ((containsSecurityCritialDataProcessors || options.Options.ContainsKey("UseNestedPacketType")) && !isNested)
{
//We set the objectToSerialise to a nested packet
//我们设置objectToSerialise对象为一个数据包
objectToSerialise = new Packet(sendingPacketTypeStr, requestReturnPacketTypeStr, payloadObject, options, true);
}
else if (isNested)
{
//Serialise the payload object into byte[]
//序列化数据包数据为字节数组
//We do not use any data processors at this stage as the object will be processed again one level higher.
//在此阶段 我们没有使用数据处理器
#if NETFX_CORE
_payloadObjectBytes = options.DataSerializer.SerialiseDataObject(payloadObject).ThreadSafeStream.ToArray();
_payloadSize = _payloadObjectBytes.Length;
#else
NetworkCommsDotNet.Tools.StreamTools.ThreadSafeStream tempStream = options.DataSerializer.SerialiseDataObject(objectToSerialise).ThreadSafeStream;
_payloadObjectBytes = tempStream.GetBuffer();
_payloadSize = (int)tempStream.Length;
#endif
//Set the packet header
//THe nulls represent internal SendReceiveOptions and no checksum
//设置数据包包头
//下面语句中的2个null参数代表使用 内部收发参数 和不使用检验和
this._packetHeader = new PacketHeader(sendingPacketTypeStr, _payloadSize, null, requestReturnPacketTypeStr, null);
//Set the deserialiser information in the nested packet header, excluding data processors
//在嵌套数据包包头中设置反序列化相关信息 不包括数据处理器
this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(options.DataSerializer, null));
}
//If we are at the top level packet we can finish off the serialisation
//对于非嵌套类型的数据包 我们直接完成序列化
if (!isNested)
{
//Set the payload stream data.
//设置有效载荷数据流。
)
//Only if there are no data processors can we use a zero length array for nulls
//This ensures that should there be any required padding we can include it
//如果没有数据处理器 options.DataProcessors.Count等于0
this.payloadStream = (StreamTools.StreamSendWrapper)objectToSerialise;
else
{
if (objectToSerialise is Packet)
//We have to use the internal explicit serializer for nested packets (the nested data is already byte[])
//针对嵌套数据 我们使用内部显式的序列化方法 嵌套的数据已经是字节数组
this.payloadStream = NetworkComms.InternalFixedSendReceiveOptions.DataSerializer.SerialiseDataObject(objectToSerialise, options.DataProcessors, options.Options);
else
this.payloadStream = options.DataSerializer.SerialiseDataObject(objectToSerialise, options.DataProcessors, options.Options);
}
//We only calculate the checkSum if we are going to use it
//当需要使用时计算检验和
string hashStr = null;
if (NetworkComms.EnablePacketCheckSumValidation)
hashStr = StreamTools.MD5(payloadStream.ThreadSafeStream.ToArray(payloadStream.Start, payloadStream.Length));
//Choose the sending and receiving packet type depending on if it is being used with a nested packet
//如果为嵌套类型 则发送的数据包类型为"嵌套类型" 否则设定为实际的消息发送类型和消息接收类型
string _sendingPacketTypeStr;
string _requestReturnPacketTypeStr = null;
if (containsSecurityCritialDataProcessors || options.Options.ContainsKey("UseNestedPacketType"))
_sendingPacketTypeStr = Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.NestedPacket);
else
{
_sendingPacketTypeStr = sendingPacketTypeStr;
_requestReturnPacketTypeStr = requestReturnPacketTypeStr;
}
this._packetHeader = new PacketHeader(_sendingPacketTypeStr, payloadStream.Length, options, _requestReturnPacketTypeStr, hashStr);
//Add an identifier specifying the serialisers and processors we have used
//在数据包包头中设定相应的"数据包序列化器和处理器"
if (objectToSerialise is Packet)
this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(NetworkComms.InternalFixedSendReceiveOptions.DataSerializer, options.DataProcessors));
else
this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(options.DataSerializer, options.DataProcessors));
}
//Set the null data header section if required
//如果数据包数据为NULL 在包头部门标明
if (objectToSerialiseIsNull &&
((!containsSecurityCritialDataProcessors && !options.Options.ContainsKey("UseNestedPacketType")) || isNested))
this._packetHeader.SetOption(PacketHeaderStringItems.NullDataSection, "");
if (NetworkComms.LoggingEnabled)
{
if (isNested)
NetworkComms.Logger.Trace(" ... created nested packet of type " + sendingPacketTypeStr);
else
NetworkComms.Logger.Trace(" ... created packet of type " + sendingPacketTypeStr + ". PacketObject data size is " + payloadStream.Length.ToString() + " bytes");
}
}
/// <inheritdoc /> ///数据包包头
public PacketHeader PacketHeader
{
get { return _packetHeader; }
}
/// <inheritdoc /> //数据包的数据部分
public StreamTools.StreamSendWrapper PacketData
{
get { return payloadStream; }
}
/// <inheritdoc /> //用于生成二级制的包头
public byte[] SerialiseHeader(SendReceiveOptions options)
{
if (options == null) throw new ArgumentNullException("options", "Provided SendReceiveOptions cannot be null.");
//We need to start of by serialising the header
//把包头序列化为二进制数组
byte[] serialisedHeader;
using (StreamTools.StreamSendWrapper sendWrapper = options.DataSerializer.SerialiseDataObject(_packetHeader, options.DataProcessors, null))
serialisedHeader = sendWrapper.ThreadSafeStream.ToArray();
> byte.MaxValue)
throw new SerialisationException("Unable to send packet as header size is larger than Byte.MaxValue. Try reducing the length of provided packetTypeStr or turning off checkSum validation.");
//The first byte now specifies the header size (allows for variable header size)
//包头转化成的二进制数据,第一个字节的值,设定为包头的长度
serialisedHeader[] = ();
if (serialisedHeader == null)
throw new SerialisationException("Serialised header bytes should never be null.");
return serialisedHeader;
}
/// <inheritdoc />
public void Dispose()
{
payloadStream.Dispose();
}
#region IExplicitlySerialize Members
/// <inheritdoc /> //序列化
public void Serialize(Stream outputStream)
{
_packetHeader.Serialize(outputStream);
outputStream.Write(BitConverter.GetBytes(_payloadSize), , sizeof(int));
outputStream.Write(_payloadObjectBytes, , _payloadSize);
}
/// <inheritdoc /> //反序列化
public void Deserialize(Stream inputStream)
{
PacketHeader.Deserialize(inputStream, out _packetHeader);
byte[] payloadLengthData = new byte[sizeof(int)];
inputStream.Read(payloadLengthData, , sizeof(int));
_payloadSize = BitConverter.ToInt32(payloadLengthData, );
_payloadObjectBytes = new byte[_payloadSize];
inputStream.Read(_payloadObjectBytes, , _payloadSize);
}
#endregion
/// <summary>
/// Deserializes from a memory stream to a <see cref="Packet"/> object
/// 把内存流反序列化为数据包
/// </summary>
/// <param name="inputStream">The memory stream containing the serialized <see cref="Packet"/></param>
/// <param name="result">The deserialized <see cref="Packet"/></param>
public static void Deserialize(Stream inputStream, out Packet result)
{
result = new Packet();
result.Deserialize(inputStream);
}
}
介绍开源的.net通信框架NetworkComms框架 源码分析(四)Packet的更多相关文章
- 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()) #阅读源码: #左侧工程栏--- ...
- ④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 ...
- Android 应用框架层 SQLite 源码分析
概述 Android 在应用框架层为开发者提供了 SQLite 相关操作接口,其归属于android.database.sqlite包底下,主要包含SQLiteProgram, SQLiteDat ...
随机推荐
- RabbitMQ(五) -- topics
RabbitMQ(五) -- topics `rabbitmq`中的`topic exchange`将路由键和某模式进行匹配,从而类似于正则匹配的方式去接收喜欢的信息. topic exchange ...
- 做NavMesh相关工作时收集的一些文章
三角形拾取 http://www.blackpawn.com/texts/pointinpoly/default.htmlCS NavMesh使用手册https://developer.valveso ...
- 继电器是如何成为CPU的(2)
继电器是如何成为CPU的(2) ——<穿越计算机的迷雾>整理和总结 上一篇已经从电池.开关.灯泡和继电器开始,画出了设计CPU所需的基本器件.这些器件将成为设计CPU的砖瓦木料.这一篇就用 ...
- 在js中对时间类型格式化字符串
Date.prototype.toString = function (format) { if (format == null) { format = "yyyy-MM-dd HH:mm: ...
- struts2学习笔记之九:struts2的命名空间
struts2的命名空间适用于多人开发,根据不同模块命名不同的命名空间,方便开发和管理 struts2如果没有配置命名空间,默认命名空间为"/",Struts2中Action的完整 ...
- 我所了解的chrome
Chrome的隐身模式 先来说说隐身模式的启用方法吧 1.键盘快捷:Ctrl + Shift + N. 2.在Windows7下的任务栏处,右击“Chrome”图标,会出一个下拉菜单,点击“新建隐身窗 ...
- Mysql 函数分类
比较重要的 1 REVERSE(s) 将字符串s的顺序反过来 2 TRIM(s) 去掉字符串s开始和结尾处的空格 一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ...
- 每天一个linux命令(17):whereis 命令
whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数,则返回所有信息. 和find相比,whereis查找的速度非 ...
- 新手入门:史上最全Web端即时通讯技术原理详解
前言 有关IM(InstantMessaging)聊天应用(如:微信,QQ).消息推送技术(如:现今移动端APP标配的消息推送模块)等即时通讯应用场景下,大多数都是桌面应用程序或者native应用较为 ...
- Mybatis中SqlMapper配置的扩展与应用(1)
奋斗了好几个晚上调试程序,写了好几篇博客,终于建立起了Mybatis配置的扩展机制.虽然扩展机制是重要的,然而如果没有真正实用的扩展功能,那也至少是不那么鼓舞人心的,这篇博客就来举几个扩展的例子. 这 ...