c#网络通信框架networkcomms内核解析之八 数据包的核心处理器
本文基于networkcomms2.3.1开源版本 gplv3协议
我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制数据 中,主程序把PacketBuilder 中的数据交给核心处理器处理的过程
//创建优先级队列项目 PriorityQueueItem item = new PriorityQueueItem(priority, this, topPacketHeader, packetBuilder.ReadDataSection(packetHeaderSize, topPacketHeader.PayloadPacketSize), incomingPacketSendReceiveOptions); NetworkComms.CompleteIncomingItemTask(item);
上面的代码中
第一行 生成了一个优先级队列项目 (主要方便交给带有优先级的自定义线程池进行处理)
其参数包括
1:优先级 2:相关Tcp连接 (有返回数据的话,还要通过此连接返回) 3:数据包包头 4:数据包的二进制数据(内存流,还没有解析成数据包,留待下一步解析)。
自定义线程池,暂时不表,看一下核心处理器。(自定义线程池,是创建多个线程,并按照优先级对数据进行处理)
NetworkComms.CompleteIncomingItemTask(item);
/// <summary>
/// Once we have received all incoming data we handle it further. This is performed at the global level to help support different priorities.
/// 数据包的核心处理器
/// </summary>
/// <param name="itemAsObj">Possible PriorityQueueItem. If null is provided an item will be removed from the global item queue</param>
internal static void CompleteIncomingItemTask(object itemAsObj)
{
if (itemAsObj == null)
throw new ArgumentNullException("itemAsObj", "Provided parameter itemAsObj cannot be null.");
//优先级队列项目
PriorityQueueItem item = null;
try
{
//If the packetBytes are null we need to ask the incoming packet queue for what we should be running
//把对象还原成优先级队列项目
item = itemAsObj as PriorityQueueItem;
if (item == null)
throw new InvalidCastException("Cast from object to PriorityQueueItem resulted in null reference, unable to continue.");
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Handling a " + item.PacketHeader.PacketType + " packet from " + item.Connection.ConnectionInfo + " with a priority of " + item.Priority.ToString() + ".");
#if !WINDOWS_PHONE
if (Thread.CurrentThread.Priority != (ThreadPriority)item.Priority) Thread.CurrentThread.Priority = (ThreadPriority)item.Priority;
#endif
//Check for a shutdown connection
if (item.Connection.ConnectionInfo.ConnectionState == ConnectionState.Shutdown) return;
//We only look at the check sum if we want to and if it has been set by the remote end
//如果这是一个检验和消息
if (NetworkComms.EnablePacketCheckSumValidation && item.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash))
{
var packetHeaderHash = item.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash);
//Validate the checkSumhash of the data
string packetDataSectionMD5 = NetworkComms.MD5Bytes(item.DataStream);
if (packetHeaderHash != packetDataSectionMD5)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn(" ... corrupted packet detected, expected " + packetHeaderHash + " but received " + packetDataSectionMD5 + ".");
//We have corruption on a resend request, something is very wrong so we throw an exception.
if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend)) throw new CheckSumException("Corrupted md5CheckFailResend packet received.");
if (item.PacketHeader.PayloadPacketSize < NetworkComms.CheckSumMismatchSentPacketCacheMaxByteLimit)
{
//Instead of throwing an exception we can request the packet to be resent
Packet returnPacket = new Packet(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend), packetHeaderHash, NetworkComms.InternalFixedSendReceiveOptions);
item.Connection.SendPacket(returnPacket);
//We need to wait for the packet to be resent before going further
return;
}
else
throw new CheckSumException("Corrupted packet detected from " + item.Connection.ConnectionInfo + ", expected " + packetHeaderHash + " but received " + packetDataSectionMD5 + ".");
}
}
//Remote end may have requested packet receive confirmation so we send that now
//如果对象发送的消息,需要确认收到,在此处进行确认
if (item.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired))
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... sending requested receive confirmation packet.");
var hash = item.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash) ? item.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash) : "";
Packet returnPacket = new Packet(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), hash, NetworkComms.InternalFixedSendReceiveOptions);
item.Connection.SendPacket(returnPacket);
}
//处理保留数据类型的数据包
//We can now pass the data onto the correct delegate
//First we have to check for our reserved packet types
//The following large sections have been factored out to make reading and debugging a little easier
if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend))
item.Connection.CheckSumFailResendHandler(item.DataStream);
else if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.ConnectionSetup))
item.Connection.ConnectionSetupHandler(item.DataStream);
else if (item.PacketHeader.PacketType == Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.AliveTestPacket) &&
(NetworkComms.InternalFixedSendReceiveOptions.DataSerializer.DeserialiseDataObject<byte[]>(item.DataStream,
NetworkComms.InternalFixedSendReceiveOptions.DataProcessors,
NetworkComms.InternalFixedSendReceiveOptions.Options))[] == )
{
//If we have received a ping packet from the originating source we reply with true
Packet returnPacket = ] { }, NetworkComms.InternalFixedSendReceiveOptions);
item.Connection.SendPacket(returnPacket);
}
//We allow users to add their own custom handlers for reserved packet types here
//else
if (true)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Triggering handlers for packet of type '" + item.PacketHeader.PacketType + "' from " + item.Connection.ConnectionInfo);
//We trigger connection specific handlers first
//触发连接上的消息处理器
bool connectionSpecificHandlersTriggered = item.Connection.TriggerSpecificPacketHandlers(item.PacketHeader, item.DataStream, item.SendReceiveOptions);
//We trigger global handlers second
//触发具体的数据包处理器
NetworkComms.TriggerGlobalPacketHandlers(item.PacketHeader, item.Connection, item.DataStream, item.SendReceiveOptions, connectionSpecificHandlersTriggered);
}
}
catch (CommunicationException)
{
if (item != null)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("A communcation exception occured in CompleteIncomingPacketWorker(), connection with " + item.Connection.ConnectionInfo + " be closed.");
item.Connection.CloseConnection();
}
}
catch (DuplicateConnectionException ex)
{
if (item != null)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn(ex.Message != null ? ex.Message : "A possible duplicate connection was detected with " + item.Connection + ". Closing connection.");
item.Connection.CloseConnection();
}
}
catch (Exception ex)
{
NetworkComms.LogError(ex, "CompleteIncomingItemTaskError");
if (item != null)
{
//If anything goes wrong here all we can really do is log the exception
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An unhandled exception occured in CompleteIncomingPacketWorker(), connection with " + item.Connection.ConnectionInfo + " be closed. See log file for more information.");
item.Connection.CloseConnection();
}
}
finally
{
//We need to dispose the data stream correctly
if (item!=null) item.DataStream.Close();
#if !WINDOWS_PHONE
//Ensure the thread returns to the pool with a normal priority
if (Thread.CurrentThread.Priority != ThreadPriority.Normal) Thread.CurrentThread.Priority = ThreadPriority.Normal;
#endif
}
}
上面的方法中,我们看到TriggerGlobalPacketHandlers方法,此方法把数据包,和我们在服务器端自定义的数据包处理器关联起来,并用数据包处理器对数据包进行处理
//触发具体的数据包处理器
NetworkComms.TriggerGlobalPacketHandlers(item.PacketHeader, item.Connection, item.DataStream, item.SendReceiveOptions, connectionSpecificHandlersTriggered);
我们看一下此方法的具体内容:
internal static void TriggerGlobalPacketHandlers(PacketHeader packetHeader, Connection connection, MemoryStream incomingDataStream, SendReceiveOptions options, bool ignoreUnknownPacketTypeOverride = false)
{
try
{
if (options == null) throw new PacketHandlerException("Provided sendReceiveOptions should not be null for packetType " + packetHeader.PacketType);
//We take a copy of the handlers list incase it is modified outside of the lock
List<IPacketTypeHandlerDelegateWrapper> handlersCopy = null;
lock (globalDictAndDelegateLocker)
if (globalIncomingPacketHandlers.ContainsKey(packetHeader.PacketType))
//根据数据包包头中的消息类型,获取相关的自定义处理器,处理器可以一个,可以多个,当然一般是一个:)
handlersCopy = new List<IPacketTypeHandlerDelegateWrapper>(globalIncomingPacketHandlers[packetHeader.PacketType]);
if (handlersCopy == null && !IgnoreUnknownPacketTypes && !ignoreUnknownPacketTypeOverride)
{
//We may get here if we have not added any custom delegates for reserved packet types
bool isReservedType = false;
; i < reservedPacketTypeNames.Length; i++)
{
if (reservedPacketTypeNames[i] == packetHeader.PacketType)
{
isReservedType = true;
break;
}
}
if (!isReservedType)
{
//Change this to just a log because generally a packet of the wrong type is nothing to really worry about
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Warn("The received packet type '" + packetHeader.PacketType + "' has no configured handler and network comms is not set to ignore unknown packet types. Set NetworkComms.IgnoreUnknownPacketTypes=true to prevent this error.");
LogError(new UnexpectedPacketTypeException("The received packet type '" + packetHeader.PacketType + "' has no configured handler and network comms is not set to ignore unknown packet types. Set NetworkComms.IgnoreUnknownPacketTypes=true to prevent this error."), "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
}
return;
}
else if (handlersCopy == null && (IgnoreUnknownPacketTypes || ignoreUnknownPacketTypeOverride))
//If we have received and unknown packet type and we are choosing to ignore them we just finish here
return;
else
{
//Idiot check
)
throw new PacketHandlerException("An entry exists in the packetHandlers list but it contains no elements. This should not be possible.");
//Deserialise the object only once
//把数据包解析处理
].DeSerialize(incomingDataStream, options);
//Pass the data onto the handler and move on.
if (LoggingEnabled) logger.Trace(" ... passing completed data packet of type '" + packetHeader.PacketType + "' to " + handlersCopy.Count.ToString() + " selected global handlers.");
//Pass the object to all necessary delgates
//We need to use a copy because we may modify the original delegate list during processing
foreach (IPacketTypeHandlerDelegateWrapper wrapper in handlersCopy)
{
try
{
//用数据包数据里处理数据包
//这里的处理器,就是我们自定义的处理器了
wrapper.Process(packetHeader, connection, returnObject);
}
catch (Exception ex)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An unhandled exception was caught while processing a packet handler for a packet type '" + packetHeader.PacketType + "'. Make sure to catch errors in packet handlers. See error log file for more information.");
NetworkComms.LogError(ex, "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
}
}
if (LoggingEnabled) logger.Trace(" ... all handlers for packet of type '" + packetHeader.PacketType + "' completed.");
}
}
catch (Exception ex)
{
//If anything goes wrong here all we can really do is log the exception
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("An exception occured in TriggerPacketHandler() for a packet type '" + packetHeader.PacketType + "'. See error log file for more information.");
NetworkComms.LogError(ex, "PacketHandlerErrorGlobal_" + packetHeader.PacketType);
}
}
什么是自定义的数据包处理器呢?
举个例子
客户端我们这么写:
ResMsgContract resMsg = newTcpConnection.SendReceiveObject<ResMsgContract>(, contract);
服务器端我们就要写一个对应的自定义数据包处理器,写法如下:
首先要注册自定义的数据包处理器:
NetworkComms.AppendGlobalIncomingPacketHandler<LoginContract>("ReqLogin", IncomingLoginRequest);
private void IncomingLoginRequest(PacketHeader header, Connection connection, LoginContract loginContract)
{
try
{
string resMsg="";
//为了简单,这里不调用数据库,而是模拟一下登录
")
resMsg = "登录成功";
else
resMsg = "用户名密码错误";
//把返回结果写入到契约类中,后面返回给客户端
ResMsgContract contract = new ResMsgContract();
contract.Message = resMsg;
connection.SendObject("ResLogin", contract);
}
catch (Exception ex)
{
}
}
IncomingLoginRequest
通过上面的注册语句,Netowrkcomms中就把消息类型“ReqLogin"与他对应的处理方法关联起来,当以后服务器收到数据包包头中消息类型为"ReqLogin"类型的消息,就知道调用哪个方法来进行处理.
下一篇分析一下,自定义数据包处理器的底层运行机制
www.cnblogs.com/networkcomms
www.networkcomms.cn
c#网络通信框架networkcomms内核解析之八 数据包的核心处理器的更多相关文章
- c#网络通信框架networkcomms内核解析 序言
NetworkComms网络通信框架序言 networkcomms是我遇到的写的最优美的代码,很喜欢,推荐给大家:) 基于networkcomms2.3.1开源版本( gplv3)协议,写了一些文章, ...
- c#网络通信框架networkcomms内核解析之十 支持优先级的自定义线程池
NetworkComms网络通信框架序言 本例基于networkcomms2.3.1开源版本 gplv3协议 如果networkcomms是一顶皇冠,那么CommsThreadPool(自定义线程池 ...
- c#网络通信框架networkcomms内核解析之六 处理接收到的二进制数据
本文基于networkcomms2.3.1开源版本 gplv3协议 在networkcomms通信系统中,服务器端收到某连接上的数据后,数据会暂时存放在"数据包创建器"(Pack ...
- c#网络通信框架networkcomms内核解析之一 消息传送
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之一 消息传送2
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之三 消息同步调用
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 客户端发送消息给服务器,服务器计算结果返回 ...
- c#网络通信框架networkcomms内核解析之九 自定义处理方法的运行机制
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们自己写的处理方法都称之为自定义处理方法 比如,我们在服务器上写的与登陆相关的处理方法 ...
- ubuntu下解析udt数据包
udt是通过udp进行端到端可靠传输的一个协议,有其默认拥塞控制算法. 之前ubuntu下wireshark的版本是1.10,不能直接解析udt数据包[1],升级到最新的2.0.0即可过滤udt数据包 ...
- Android-Volley网络通信框架(二次封装数据请求和图片请求(包含处理请求队列和图片缓存))
1.回想 上篇 使用 Volley 的 JsonObjectRequest 和 ImageLoader 写了 电影列表的样例 2.重点 (1)封装Volley 内部 请求 类(请求队列,数据请求,图片 ...
随机推荐
- FLASH CC 2015 CANVAS 实际应用过程中遇到的【粉色】问题(不定期更新)
1,导入音乐导致发布卡死 一开始以为是不支持,FQ搜索了一些帖子,也有说不能再时间轴加音乐,需要用代码加入,想想不太可能啊,如果真的不能为什么IDE不禁用呢? 而实际问题是: 我使用的其中一条音效有问 ...
- centos下安装nginx和php-fpm
安装这两个花了大约七个小时,简直呵呵,安装nginx就是直接 yum install nginx ,但发现一打开php文件就是直接下载该php文件,也就是不能识别php文件,解决这个花了好久,但其实看 ...
- 关于C++类中的成员
突然发现,如果C++的类成员中存在共有的成员,则可以通过指针的偏移来访问私有的成员变量,当然前提是对内存对齐比较清楚.只要骗过了编译器就可以为所欲为了. #include <cstdio> ...
- JCO事务管理
/* * 标准对账单过账 * @account 标准对账单号 * @year 年度 */ public List<String> doAccountStatmentPost(String ...
- Android_安装GooglePlay
百度搜索:“google play 安装” http://jingyan.baidu.com/article/cbf0e500f4645b2eab28935a.html http://samsungb ...
- 基于Linux的oracle数据库管理 part6 (backup 相关的脚本)
这里只是简单的介绍几种 备份方法 备份: 逻辑备份, 冷备份, 热备份 逻辑备份 也称作 导入(import), 导出(export), 作用是在不同的oracle数据库之间转移数据 物理备份, 就是 ...
- 2014 Multi-University Training Contest 4
1006 hdu4902 #include <iostream> #include<stdio.h> #include<vector> #include<qu ...
- bignum 大数模板
今天无意间看到一个很好的大数模板,能算加.减.乘.除等基本运算,但操作减法的时候只能大数减小数,也不支持负数,如果是两个负数的话去掉符号相加之后再取反就可以了,一正一负比较绝对值大小,然后相减.我借用 ...
- 转载: C++ 转换构造函数 和 类型转换函数
1.对于系统的预定义基本类型数据,C++提供了两种类型转换方式:隐式类型转换和显式类型转换. ,sum; double b=5.55; sum=a+b;//-------(1) std::cout&l ...
- 工作了3年的JAVA程序员应该具备什么技能?(zhuan)
http://www.500d.me/article/5441.html **************************************** 来源:五百丁 作者:LZ2016-03-18 ...