介绍开源的.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 ...
随机推荐
- [Beautifulzzzz的博客目录] 快速索引点这儿O(∩_∩)O~~,红色标记的是不错的(⊙o⊙)哦~
3D相关开发 [direct-X] 1.direct-X最小框架 [OpenGL] 1.环境搭建及最小系统 [OpenGL] 2.企业版VC6.0自带的Win32-OpenGL工程浅析 51单片机 [ ...
- 自动更新Chromium
Chromium 其实就是开发版本的Chrome, 即Chrome dev 版本.一般他的版本要比正式版的Chrome高两个及以上.比如正式版本现在是29,开发者版本已经是32了. 这表示很多新功能你 ...
- zk系列-zookeeper的使用
zk支持java/c访问,java常用的有apache-zkclient.社区版的i0tec-zkclient.github.adyliu,apache-zkclient是zk自身提供的接口,i0te ...
- java.util.Properties
1 Properties文件中分隔符及空格的处理 因为 Properties 继承于 Hashtable,所以可对 Properties 对象应用 put 和 putAll 方法.但强烈反对使用这两个 ...
- ios 下创建,删除文件夹的方法
NSString *imageDir = [NSString stringWithFormat:@"%@/Caches/%@", NSHomeDirectory(), dirNam ...
- Atitit ACID解决方案2PC(两阶段提交) 跨越多个数据库实例的ACID保证
Atitit ACID解决方案2PC(两阶段提交) 跨越多个数据库实例的ACID保证 1.1. ACID解决方案1 1.2. 数据库厂商在很久以前就认识到数据库分区的必要性,并引入了一种称为2PC( ...
- 大数据时代的IT架构设计
大数据时代的IT架构设计(来自互联网.银行等领域的一线架构师先进经验分享) IT架构设计研究组 编著 ISBN 978-7-121-22605-2 2014年4月出版 定价:49.00元 208页 ...
- [转] SSH原理与运用(2):远程操作与端口转发
英文:阮一峰 链接:http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html 接着前一次的文章,继续介绍SSH的用法. (Imag ...
- css text-align-last设置末尾文本对齐方式
text-align-last:auto | start | end | left | right | center | justify auto: 无特殊对齐方式. left: 内容左对齐. cen ...
- 深入理解PHP内核(三)概览-SAPI概述
本文链接:http://www.orlion.ml/234/ 1.在PHP生命周期的各个阶段,一些与服务相关的操作都是通过SAPI接口实现.这些内置实现的物理位置在PHP源码的SAPI目录.这个目录存 ...