介绍开源的.net通信框架NetworkComms框架 源码分析(三)PacketHeader
原文网址: http://www.cnblogs.com/csdev
Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是:Apache License v2
开源地址是:https://github.com/MarcFletcher/NetworkComms.Net
PacketHeader 数据包头
这个类,就是数据包的包头部分,在使用networkcomms通信框架时,发送数据时,框架会首先把数据转化成数据包(Packet).数据包包含(PacketHeader+包体部分)
首先来看2个枚举类
数据包包头中,Long类型的项.包括数据包包体部分的大小TotalPayloadSize,
序列化处理器 SerializerProcessors,包的顺序号PacketSequenceNumber,包的创建时间PacketCreationTime
/// <summary>
/// Any <see cref="PacketHeader"/> options which are stored as a long.
/// 数据包包头的一些参数选项 Long类型
/// </summary>
public enum PacketHeaderLongItems
{
/// <summary>
/// The total size of the packet data payload in bytes. This is a compulsory option.
/// 数据包大小 此项为必需项目
/// </summary>
TotalPayloadSize,
/// <summary>
/// The data serialiser and data processor used to unwrap the payload. Used as flags.
/// 数据序列化器和处理器相对应的值
/// 这里解释一下 每个序列化器和处理器都对应一个值 通过计算合成此项参数 通过此项参数也能分析出使用了哪些序列化器和处理器
/// </summary>
SerializerProcessors,
/// <summary>
/// The sequence number for this packet. Each connection maintains a unique counter which is increments on each sent packet. This is a compulsory option.
/// 顺序号 每个连接维护一个单独的计数器 此顺序号每次发送数据包后都自增长 也就是每个数据包都有一个唯一的顺序号 此项为必需项目
/// </summary>
PacketSequenceNumber,
/// <summary>
/// The creation time of the packet header.
/// 数据包包头创建时间
/// </summary>
PacketCreationTime,
}
包头中字符类型的项
数据包的消息类型 PacketType
对方收到消息后,是否需要回复 ReceiveConfirmationRequired
如果需要回复类型 RequestedReturnPacketType
数据包对应的检验和 CheckSumHash
SourceNetworkIdentifier 网络ID 每个连接是唯一的
/// <summary>
/// Any <see cref="PacketHeader"/> options which are stored as a string.
/// 数据包包头参数 字符类型
/// </summary>
public enum PacketHeaderStringItems
{
/// <summary>
/// The type of the packet. This is a compulsory option which determines how the incoming packet is handled.
/// 数据包的消息类型 此项为必需项目 对方根据数据包的消息类型选择相对应的处理器
/// </summary>
PacketType,
/// <summary>
/// Specifies if a receive confirmation is required for this packet. String option as takes up less space for a boolean option.
/// 对方收到后,是否需要发送确认消息 字符串选项占用更少的空间
/// </summary>
ReceiveConfirmationRequired,
/// <summary>
/// The packet type which should be used for any return packet type.
/// 如果发送的是同步消息,即需要接收对方返回的结果 指定预期返回的消息类型
/// </summary>
RequestedReturnPacketType,
/// <summary>
/// A checksum corresponding to the payload data.
/// 数据包对应的检验和
/// </summary>
CheckSumHash,
/// <summary>
/// The network identifier of the packet source
/// 网络ID
/// </summary>
SourceNetworkIdentifier,
/// <summary>
/// Optional packet identifier.
/// 数据包ID 可选
/// </summary>
PacketIdentifier,
/// <summary>
/// The data section should be interpreted as a null
/// 数据包为Null
/// </summary>
NullDataSection,
}
数据包包头 PacketHeader
/// <summary>
/// Contains information required to send, receive and correctly rebuild any objects sent via NetworkComms.Net.
/// Any data sent via NetworkCommsDotNet is always preceded by a packetHeader.
/// 数据包包头 包含发送,接收,重建二进制数据为数据包的相关信息
/// 通过networkcomms框架发送的数据,总是先发送数据包包头,再发送数据包
/// 数据包包头的第一个字节,存储数据包包头的长度
/// </summary>
public sealed class PacketHeader : IExplicitlySerialize
{
Dictionary<PacketHeaderLongItems, long> longItems;
Dictionary<PacketHeaderStringItems, string> stringItems;
/// <summary>
/// Blank constructor required for deserialisation
/// 构造函数 反序列化时使用
/// </summary>
#if ANDROID || iOS
[Preserve]
#endif
private PacketHeader() { }
/// <summary>
/// Creates a new packetHeader
/// 创建一个新的数据包包头
/// </summary>
/// <param name="packetTypeStr">数据包的消息类型 The packet type to be used.</param>
/// <param name="payloadPacketSize">数据包的大小 The size on bytes of the payload</param>
/// <param name="sendReceiveOptions">收发参数 Send receive options which may contain header relevant options.</param>
/// <param name="requestedReturnPacketTypeStr">返回的消息类型 An optional field representing the expected return packet type</param>
/// <param name="checkSumHash">检验和 An optional field representing the payload checksum</param>
public PacketHeader(string packetTypeStr, long payloadPacketSize, SendReceiveOptions sendReceiveOptions = null, string requestedReturnPacketTypeStr = null, string checkSumHash = null)
{
if (packetTypeStr == requestedReturnPacketTypeStr)
throw new ArgumentException("The provided packetTypeStr and requestedReturnPacketTypeStr parameters must be different.");
longItems = new Dictionary<PacketHeaderLongItems, long>();
stringItems = new Dictionary<PacketHeaderStringItems, string>();
stringItems.Add(PacketHeaderStringItems.PacketType, packetTypeStr);
longItems.Add(PacketHeaderLongItems.TotalPayloadSize, payloadPacketSize);
)
throw new Exception("payloadPacketSize can not be less than 0.");
if (requestedReturnPacketTypeStr != null)
stringItems.Add(PacketHeaderStringItems.RequestedReturnPacketType, requestedReturnPacketTypeStr);
if (checkSumHash != null)
stringItems.Add(PacketHeaderStringItems.CheckSumHash, checkSumHash);
if (sendReceiveOptions != null)
{
if (sendReceiveOptions.Options.ContainsKey("ReceiveConfirmationRequired"))
stringItems.Add(PacketHeaderStringItems.ReceiveConfirmationRequired, "");
if (sendReceiveOptions.Options.ContainsKey("IncludePacketConstructionTime"))
longItems.Add(PacketHeaderLongItems.PacketCreationTime, DateTime.Now.Ticks);
}
}
/// <summary>
/// Constructor used for deserialisation
/// 用于反序列化的构造函数
/// </summary>
/// <param name="packetHeaderStream"></param>
/// <param name="headerSendReceiveOptions"></param>
internal PacketHeader(MemoryStream packetHeaderStream, SendReceiveOptions headerSendReceiveOptions)
{
try
{
if (packetHeaderStream == null) throw new ArgumentNullException("packetData", "Provided MemoryStream parameter cannot be null.");
if (headerSendReceiveOptions == null) throw new ArgumentNullException("sendReceiveOptions", "Provided SendReceiveOptions parameter cannot be null.");
)
throw new SerialisationException("Attempted to create packetHeader using byte[0].");
PacketHeader tempObject = headerSendReceiveOptions.DataSerializer.DeserialiseDataObject<PacketHeader>(packetHeaderStream, headerSendReceiveOptions.DataProcessors, headerSendReceiveOptions.Options);
if (tempObject == null || !tempObject.longItems.ContainsKey(PacketHeaderLongItems.TotalPayloadSize) || !tempObject.stringItems.ContainsKey(PacketHeaderStringItems.PacketType))
throw new SerialisationException("Failed to deserialize a valid packet header. Deserialized header result was null or did not contain the compulsory fields, TotalPayloadSize and PacketType.");
else
{
stringItems = new Dictionary<PacketHeaderStringItems, string>();
foreach (var pair in tempObject.stringItems)
stringItems.Add(pair.Key, pair.Value);
longItems = new Dictionary<PacketHeaderLongItems, long>();
foreach (var pair in tempObject.longItems)
longItems.Add(pair.Key, pair.Value);
}
}
catch (Exception ex)
{
NetworkCommsDotNet.Tools.LogTools.LogException(ex, "PacketHeaderDeserialisationError", "The header data follows:" + BitConverter.ToString(packetHeaderStream.ToArray()));
throw new SerialisationException("Error deserializing packetHeader. " + ex.ToString());
}
}
#region Get & Set
/// <summary>
/// The total size in bytes of the payload.
/// 数据包的大小
/// </summary>
public int TotalPayloadSize
{
get { return (int)longItems[PacketHeaderLongItems.TotalPayloadSize]; }
}
/// <summary>
/// The packet type.
/// 数据包的类型
/// </summary>
public string PacketType
{
get { return stringItems[PacketHeaderStringItems.PacketType]; }
}
/// <summary>
/// The sequence number for this packet
/// 数据包的顺序号
/// </summary>
public long PacketSequenceNumber
{
get { return longItems[PacketHeaderLongItems.PacketSequenceNumber]; }
}
/// <summary>
/// The packet type which should be used for any return packet type. If no return packet type is set returns null.
/// 数据包被处理后,返回的消息类型
/// 主要用于同步方法
/// </summary>
public string RequestedReturnPacketType
{
get
{
if (stringItems.ContainsKey(PacketHeaderStringItems.RequestedReturnPacketType))
return stringItems[PacketHeaderStringItems.RequestedReturnPacketType];
else
return null;
}
}
/// <summary>
/// Optional packet identifier. If no packet identifier is set returns null.
/// 数据包ID 可选 如果没有数据包ID ,返回NULL
/// </summary>
public string PacketIdentifier
{
get
{
if (stringItems.ContainsKey(PacketHeaderStringItems.PacketIdentifier))
return stringItems[PacketHeaderStringItems.PacketIdentifier];
else
return null;
}
}
/// <summary>
/// The network identifier of the packets source peer. If no source network identifier is set returns null.
/// Also see <see cref="ConnectionInfo.NetworkIdentifier"/>.
/// 网络ID 如果没有返回NULL
/// </summary>
public string SourceNetworkIdentifier
{
get
{
if (stringItems.ContainsKey(PacketHeaderStringItems.SourceNetworkIdentifier))
return stringItems[PacketHeaderStringItems.SourceNetworkIdentifier];
else
return null;
}
}
/// <summary>
/// A checksum corresponding to the payload data. If no checksum is set returns null.
/// 数据包的检验和
/// </summary>
public string CheckSumHash
{
get
{
if (stringItems.ContainsKey(PacketHeaderStringItems.CheckSumHash))
return stringItems[PacketHeaderStringItems.CheckSumHash];
else
return null;
}
}
/// <summary>
/// Check if a string option has been set.
/// 检查某个字符型选项是否存在
/// </summary>
/// <param name="option">The string option to be checked.</param>
/// <returns>Returns true if the provided string option has been set.</returns>
public bool ContainsOption(PacketHeaderStringItems option)
{
return stringItems.ContainsKey(option);
}
/// <summary>
/// Check if a long option has been set.
/// 检查某个long类型选项是否存在
/// </summary>
/// <param name="option">The long option to be checked.</param>
/// <returns>Returns true if the provided long option has been set.</returns>
public bool ContainsOption(PacketHeaderLongItems option)
{
return longItems.ContainsKey(option);
}
/// <summary>
/// Get a long option.
/// 获取一个long类型选项
/// </summary>
/// <param name="option">The option to get</param>
/// <returns>The requested long option</returns>
public long GetOption(PacketHeaderLongItems option)
{
return longItems[option];
}
/// <summary>
/// Get a string option
/// 获取一个字符型选项
/// </summary>
/// <param name="options">The option to get</param>
/// <returns>The requested string option</returns>
public string GetOption(PacketHeaderStringItems options)
{
return stringItems[options];
}
/// <summary>
/// Set a long option with the provided value.
/// 设置一个long类型选项
/// </summary>
/// <param name="option">The option to set</param>
/// <param name="Value">The option value</param>
public void SetOption(PacketHeaderLongItems option, long Value)
{
longItems[option] = Value;
}
/// <summary>
/// Set a string option with the provided value.
/// 设置一个字符型选项
/// </summary>
/// <param name="option">The option to set</param>
/// <param name="Value">The option value</param>
public void SetOption(PacketHeaderStringItems option, string Value)
{
stringItems[option] = Value;
}
#endregion
// V3版本中通讯框架内部没有采用V2版本中使用的protobuf.net序列化方法,使得程序应用层可以很方便的更换序列化器
#region IExplicitlySerialize Members
/// <inheritdoc />
public void Serialize(Stream outputStream)
{
List<byte[]> data = new List<byte[]>();
byte[] longItemsLengthData = BitConverter.GetBytes(longItems.Count); data.Add(longItemsLengthData);
foreach (var pair in longItems)
{
byte[] keyData = BitConverter.GetBytes((int)pair.Key); data.Add(keyData);
byte[] valData = BitConverter.GetBytes(pair.Value); data.Add(valData);
}
byte[] stringItemsLengthData = BitConverter.GetBytes(stringItems.Count); data.Add(stringItemsLengthData);
foreach (var pair in stringItems)
{
byte[] keyData = BitConverter.GetBytes((int)pair.Key); data.Add(keyData);
byte[] valData = Encoding.UTF8.GetBytes(pair.Value);
byte[] valLengthData = BitConverter.GetBytes(valData.Length);
data.Add(valLengthData);
data.Add(valData);
}
foreach (byte[] datum in data)
outputStream.Write(datum, , datum.Length);
}
/// <inheritdoc />
public void Deserialize(Stream inputStream)
{
longItems = new Dictionary<PacketHeaderLongItems, long>();
stringItems = new Dictionary<PacketHeaderStringItems, string>();
, sizeof(int));
);
if (longItemsLength * (sizeof(int) + sizeof(long)) > inputStream.Length)
throw new SerialisationException("Error deserializing packet header. Number of long items was too large to be present in the input stream."+
" This error is typically thrown because a non NetworkComms.Net peer attempted to communicate. If this is desirable please consider using an unmanaged connection.");
; i < longItemsLength; i++)
{
, sizeof(int));
PacketHeaderLongItems key = (PacketHeaderLongItems)BitConverter.ToInt32(keyData, );
, sizeof(long));
);
longItems.Add(key, val);
}
, sizeof(int));
);
* sizeof(int)) > inputStream.Length)
throw new SerialisationException("Error deserializing packet header. Number of string items was too large to be present in the input stream."+
" This error is typically thrown because a non NetworkComms.Net peer attempted to communicate. If this is desirable please consider using an unmanaged connection.");
; i < stringItemsLength; i++)
{
, sizeof(int));
PacketHeaderStringItems key = (PacketHeaderStringItems)BitConverter.ToInt32(keyData, );
, sizeof(int));
);
if (valLength > inputStream.Length)
throw new SerialisationException("Error deserializing packet header. Length string item was too large to be present in the input stream."+
" This error is typically thrown because a non NetworkComms.Net peer attempted to communicate. If this is desirable please consider using an unmanaged connection.");
, valData.Length);
string val = new String(Encoding.UTF8.GetChars(valData));
stringItems.Add(key, val);
}
}
#endregion
/// <summary>
/// Deserializes from a memory stream to a <see cref="PacketHeader"/> object
/// </summary>
/// <param name="inputStream">The memory stream containing the serialized <see cref="PacketHeader"/></param>
/// <param name="result">The deserialized <see cref="PacketHeader"/></param>
public static void Deserialize(Stream inputStream, out PacketHeader result)
{
result = new PacketHeader();
result.Deserialize(inputStream);
}
}
介绍开源的.net通信框架NetworkComms框架 源码分析(三)PacketHeader的更多相关文章
- 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 ...
随机推荐
- 1、CC2541蓝牙4.0芯片中级教程——基于OSAL操作系统的运行流程了解+定时器和串口例程了解
本文根据一周CC2541笔记汇总得来—— 适合概览和知识快速索引—— 全部链接: 中级教程-OSAL操作系统\OSAL操作系统-实验01 OSAL初探 [插入]SourceInsight-工程建立方法 ...
- 虚拟化平台cloudstack(2)——安装(上)
vmware workstation安装ubuntu server12.04 这个其实没什么说的了,下软件,安装,一顿下一步,OK. 安装完成后,为ubuntu server 12.04安装桌面. 使 ...
- ssh/openssh
http://www.cnblogs.com/wwufengg/articles/ssh-openssh-detail.html http://www.cnblogs.com/jjkv3/archiv ...
- Objective-C 随机数
有个项目要给客户发送随机验证码, 试了下这样可以 srand(time()); code = [NSString stringWithFormat: - )) + ];
- Linux初学 - Elasticsearch环境安装
下载 https://www.elastic.co/downloads/elasticsearch 安装 rpm -ivh 也可以双击rpm包安装 修改elastaticsearch host配置 修 ...
- asp.net Web.config 在不同版本的IIS配置的IHttpHandler的访问路径,以及经典模式和集成模式不同的配置
如果IIS7.0使用.net4.0以上版本的框架,<system.web>中的httpHandlers节点就没有用了,而应该使用微软专为.net4.0以上版本设计的新节点<syste ...
- 练习2 练习目标-使用引用类型的成员变量:在本练习中,将扩展银行项目,添加一个(客户类)Customer类。Customer类将包含一个Account对象。
package banking; public class Customer { private String firstName; private String lastName; private ...
- agularJs 路由
angularJs的路由方式: 先定义一个模板ng-app-->然后定义路由的规则(routeProvider)在服务config里-->然后通过不同的URL实现 到单页面加载的所需页面的 ...
- javase基础复习攻略《五》
总结完JAVA的基础语法和面向对象思想后,今天为大家补充一下JAVA中的数组,数组是什么呢?大家是不是想到了集合,数组和集合有相似之处,集合中的数据无序,不可以重复.数组中则存放着具有相同特征的一组数 ...
- web接口测试之GET与POST请求
关于HTTP协议,我考虑了一下觉得没必要再花一节内容来介绍,因为网上关于HTTP协议的介绍非常详细.本着以尽量避免介绍一空洞了概念与理论来介绍接口测试,我这里仍然会给出具体实例. 在此之前先简单的介绍 ...