SuperSocket源码解析之消息处理
一 简述
Tcp消息的处理本身是与Tcp消息传输过程独立的,是消息的两个不同阶段,从前面的会话生命周期我们已经知道消息的传输主要有SocketSession实现,而真正处理则交由AppSession实现,SuperSocket的层次划分也是非常清晰明了。
SuperSocket消息处理主要流程:接收=》原始过滤=》协议解析=》命令路由并执行=》找不到命令则直接一分不动发给客户端
二 消息接收
1 开始接收
代码位置:AsyncSocketSession=》StartReceive
private void StartReceive(SocketAsyncEventArgs e)
{
StartReceive(e, );
} private void StartReceive(SocketAsyncEventArgs e, int offsetDelta)
{
bool willRaiseEvent = false; try
{
if (offsetDelta < || offsetDelta >= Config.ReceiveBufferSize)
throw new ArgumentException(string.Format("Illigal offsetDelta: {0}", offsetDelta), "offsetDelta"); var predictOffset = SocketAsyncProxy.OrigOffset + offsetDelta; if (e.Offset != predictOffset)
{
e.SetBuffer(predictOffset, Config.ReceiveBufferSize - offsetDelta);
} if (IsInClosingOrClosed)
return; OnReceiveStarted();
willRaiseEvent = Client.ReceiveAsync(e);
}
catch (Exception exc)
{
LogError(exc); OnReceiveError(CloseReason.SocketError);
return;
} if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
在接收数据前后触发ReceiveStarted,ReceiveEnd事件
2 接收有消息处理并转入处理
public void ProcessReceive(SocketAsyncEventArgs e)
{
if (!ProcessCompleted(e))
{
OnReceiveError(CloseReason.ClientClosing);
return;
} OnReceiveEnded(); int offsetDelta; try
{
//交给app会话处理接收到的数据,而appsession又交给接收请求处理器处理
offsetDelta = this.AppSession.ProcessRequest(e.Buffer, e.Offset, e.BytesTransferred, true);
}
catch (Exception exc)
{
LogError("Protocol error", exc);
this.Close(CloseReason.ProtocolError);
return;
} //read the next block of data sent from the client
StartReceive(e, offsetDelta);
}
三 消息处理
1 入口:按照协议解析,每次只处理一个数据包,因此便有了如下的入口代码
int IAppSession.ProcessRequest(byte[] readBuffer, int offset, int length, bool toBeCopied)
{
int rest, offsetDelta; while (true)
{
var requestInfo = FilterRequest(readBuffer, offset, length, toBeCopied, out rest, out offsetDelta); if (requestInfo != null)
{
try
{
AppServer.ExecuteCommand(this, requestInfo);
}
catch (Exception e)
{
HandleException(e);
}
} if (rest <= )
{
return offsetDelta;
} //Still have data has not been processed
offset = offset + length - rest;
length = rest;
}
}
2 原始过滤

此处以原始数据接收事件方式,预留给AppServer子类处理该原始数据包,意味着可以对原始数据包执行一次拦截处理,如果经过一些逻辑处理后不能满足,则将终止该数据包继续传播
3 协议解析
协议解析由AppSession的m_ReceiveFilter成员完成,该成员的实例化有2种实现方式
默认实例化 :默认命令行协议,该协议举例
下面是2行命令,行使用\r\n 作为结束标识,也就是回车换行,命令行内部使用空格分隔,第一个控制之前 如echo 为消息头也就是key,其后的字符串也使用空格分隔,作为参数,其整体=body
echo cc xxd
cdc ds mmm
解析后为2个StringRequestInfo对象

默认的协议解析工厂

实例化默认协议解析对象

通过AppServer构造函数传递解析工厂

4 命令路由
上面我们已经解析到客户端发来的2条命令分别为echo 参数为cc xxd;cdc ds mmm;
其中key分别为echo和cdc,对于命令模式来说命令本身使用name字段进行标识,如果我们的key与name匹配那么我们即可路由到一个已有的命令,进而执行该命令,来看代码
对于能够路由到的命令我们执行命令

对于路由失败来说SuperSocket又是怎么做的呢?

找不到命令来处理该消息,将该命令名字发送会客户端,意思说明服务器没有实现该命令,那么命令从何而来?
5 命令
这还的从CommandLoader说起,CommandLoader又追溯到AppServer的构建过程

在没有显示配置CommandLoader的情况下默认为ReflectCommandLoader

ReflectCommandLoader创建命令

ReflectCommandLoader将扫描应用程序根目录下所有程序,并将实现了命令接口的实例通过反射创建出来
public override bool TryLoadCommands(out IEnumerable<TCommand> commands)
{
commands = null; var commandAssemblies = new List<Assembly>(); if (m_AppServer.GetType().Assembly != this.GetType().Assembly)
commandAssemblies.Add(m_AppServer.GetType().Assembly); string commandAssembly = m_AppServer.Config.Options.GetValue("commandAssembly"); if (!string.IsNullOrEmpty(commandAssembly))
{
OnError("The configuration attribute 'commandAssembly' is not in used, please try to use the child node 'commandAssemblies' instead!");
return false;
} if (m_AppServer.Config.CommandAssemblies != null && m_AppServer.Config.CommandAssemblies.Any())
{
try
{
var definedAssemblies = AssemblyUtil.GetAssembliesFromStrings(m_AppServer.Config.CommandAssemblies.Select(a => a.Assembly).ToArray()); if (definedAssemblies.Any())
commandAssemblies.AddRange(definedAssemblies);
}
catch (Exception e)
{
OnError(new Exception("Failed to load defined command assemblies!", e));
return false;
}
} if (!commandAssemblies.Any())
{
commandAssemblies.Add(Assembly.GetEntryAssembly());
} var outputCommands = new List<TCommand>(); foreach (var assembly in commandAssemblies)
{
try
{
outputCommands.AddRange(assembly.GetImplementedObjectsByInterface<TCommand>());
}
catch (Exception exc)
{
OnError(new Exception(string.Format("Failed to get commands from the assembly {0}!", assembly.FullName), exc));
return false;
}
} commands = outputCommands; return true;
}
因此默认的我们只需要定义一些实现命令接口ICommand<TAppSession, TRequestInfo>的命令出来,
6 自定义命令
直接继承CommandBase抽象类即可

到此SuperSocket对消息的处理流程差不多就是这样了,SuperSocket的框架的使用 我们只需要自定义自己的AppServer,以及AppServer配套的AppSession,ReciverFilter,以及命令等即可,这些在官方提供的例子中已经很清晰
SuperSocket源码解析之消息处理的更多相关文章
- SuperSocket源码解析之开篇
一 简介 官方介绍:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 Socket 连接和 S ...
- SuperSocket源码解析之开篇 (转)
一 简介 官方介绍:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架.你无须了解如何使用 Socket, 如何维护 Socket 连接和 S ...
- SuperSocket源码解析之启动过程
一 简介 这里主要说明从配置系统引导启动SuperScoekt作为应用程序,且以控制台程序方式启动 二 启动过程 2.1 配置解析 从读取配置文件开始,直接拿到一个SocketServiceConfi ...
- SuperSocket源码解析之配置系统
一 继承Net配置系统 Net应用程序配置机制跟程序集引用大致类似,均具有继承性,如iis几乎每个应用程序都会有一个Web.config,比如我们使用vs2012以上版本创建一个web应用程序会自带一 ...
- SuperSocket源码解析之会话生命周期
一 基本概念 会话(Session)是客户端与服务器进行通信的基本单元,也是一个Socket的封装,在http协议中也有Session机制,其主要作用封装一个通信单元socket,负责服务器与客户端消 ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 分类: QT2009-10-28 13:33 17695人阅读 评论(13) 收藏 举报 qtapplicationwindowse ...
- Celery 源码解析五: 远程控制管理
今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...
- Spark 源码解析 : DAGScheduler中的DAG划分与提交
一.Spark 运行架构 Spark 运行架构如下图: 各个RDD之间存在着依赖关系,这些依赖关系形成有向无环图DAG,DAGScheduler对这些依赖关系形成的DAG,进行Stage划分,划分的规 ...
随机推荐
- 【node.js】本地模式安装express:'express' 不是内部或外部命令,也不是可运行的程序或批处理文件。
今天闲来无事想起了node.js,因此到网上下载了一个node.js的安装程序进行安装.其中: 安装程序:node-v0.11.13-x64.msi PC系统:Windows 7 自定义安装路径:D: ...
- 工具篇-TraceView
--- layout: post title: 工具篇-TraceView description: 让我们远离卡顿和黑屏 2015-10-09 category: blog --- ## 让我们远 ...
- python中的迭代
#迭代Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上. #list这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标 ...
- JavaSE学习总结第18天_集合框架4
18.01 Map集合概述和特点 Map接口概述:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值 Map接口和Collection接口的不同 1.Map是双列的,Coll ...
- STL之stack(栈)
栈(statck)这种数据结构在计算机中是相当出名的.栈中的数据是先进后出的(First In Last Out, FILO).栈只有一个出口,允许新增元素(只能在栈顶上增加).移出元素(只能移出栈顶 ...
- HDU1004题解分析(字符串处理)
这道题是从上个星期开始做的,看到题时觉得似曾相似,好像做过,理了一下思路敲完代码又不对,后来发现是数组用错了,之后又重新想了数组和比较用法,昨天改了一个多小时,后来样例输出全部正确,所有情况都考虑到了 ...
- BZOJ 1019: [SHOI2008]汉诺塔( dp )
dp(x, y)表示第x根柱子上y个盘子移开后到哪根柱子以及花费步数..然后根据汉诺塔原理去转移... ------------------------------------------------ ...
- [LeetCode]题解(python):024-Swap Nodes in Pairs
题目来源: https://leetcode.com/problems/swap-nodes-in-pairs/ 题意分析: 给定一个链表,每两个相邻节点就行交换.比如1->2->3-&g ...
- [每日一题] 11gOCP 1z0-052 :2013-09-14 repeated parsing activity.................................A70
转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/11699299 正确答案:D SQL语句的执行过程: 1.客户端输入sql语句update ...
- 物流追踪 - -GPS和GPRS应用
源码1: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<termios.h ...