SuperSocket入门(二)- 探索AppServer、AppSession,Conmmand和App.config
在上一篇文章中,我们已经了解到了如何在SuperSocket处理客户端请求。 同时我们可能会发现一个问题,如果我们的服务器端包含有很多复杂的业务逻辑,这样的switch/case代码将会很长而且非常难看,并且没有遵循面向对象设计的原则(OOD)。 在这种情况下,SuperSocket提供了一些让我们在多个独立的类中处理各自不同的请求的命令框架,接下来我们一起来看一下怎么使用
1、自定义AppSession
AppSession 代表一个和客户端的逻辑连接,基于连接的操作应该放在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。
使用方法:创建自定义类MySession,继承AppSession类并重写AppSession类的方法(注意:一个AppSession对象对应一个连接)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using SuperSocket.Common; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Protocol; /**************************************************************** * 作者:黄昏前黎明后 * CLR版本:4.0.30319.42000 * 创建时间:2017-01-19 00:02:17 * 2017 * 描述说明:自定义连接类MySession,继承AppSession,并传入到AppSession * * 修改历史: * * *****************************************************************/ namespace SuperSocketDemo.Session { /// <summary> /// 自定义连接类MySession,继承AppSession,并传入到AppSession /// </summary> public class MySession : AppSession<MySession> { /// <summary> /// 新连接 /// </summary> protected override void OnSessionStarted() { //输出客户端IP地址 Console.WriteLine(this.LocalEndPoint.Address.ToString()); this.Send("Hello User,Welcome to SuperSocket Telnet Server!"); } /// <summary> /// 未知的Command /// </summary> /// <param name="requestInfo"></param> protected override void HandleUnknownRequest(StringRequestInfo requestInfo) { this.Send("unknow"); } /// <summary> /// 捕捉异常并输出 /// </summary> /// <param name="e"></param> protected override void HandleException(Exception e) { this.Send("error: {0}", e.Message); } /// <summary> /// 连接关闭 /// </summary> /// <param name="reason"></param> protected override void OnSessionClosed(CloseReason reason) { base.OnSessionClosed(reason); } } }
2、自定义AppServer
AppServer 代表了监听客户端连接,承载TCP连接的服务器实例。理想情况下,我们可以通过AppServer实例获取任何你想要的客户端连接,服务器级别的操作和逻辑应该定义在此类之中。
使用方法:创建自定义类MyServer,继承AppServer类并重写AppServer类的方法
using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocketDemo.Session; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /**************************************************************** * 作者:黄昏前黎明后 * CLR版本:4.0.30319.42000 * 创建时间:2017-01-19 00:15:45 * 2017 * 描述说明:自定义服务器类MyServer,继承AppServer,并传入自定义连接类MySession * * 修改历史: * * *****************************************************************/ namespace SuperSocketDemo.Server { /// <summary> /// 自定义服务器类MyServer,继承AppServer,并传入自定义连接类MySession /// </summary> public class MyServer : AppServer<MySession> { protected override void OnStartup() { base.OnStartup(); // Console.WriteLine("服务器启动"); } /// <summary> /// 输出新连接信息 /// </summary> /// <param name="session"></param> protected override void OnNewSessionConnected(MySession session) { base.OnNewSessionConnected(session); //输出客户端IP地址 Console.Write("\r\n" + session.LocalEndPoint.Address.ToString() + ":连接"); } /// <summary> /// 输出断开连接信息 /// </summary> /// <param name="session"></param> /// <param name="reason"></param> protected override void OnSessionClosed(MySession session, CloseReason reason) { base.OnSessionClosed(session, reason); Console.Write("\r\n" + session.LocalEndPoint.Address.ToString() + ":断开连接"); } protected override void OnStopped() { base.OnStopped(); Console.WriteLine("服务已停止"); } } }
3、使用Command
在SuperSocket 中的Command让我们进行扩展,使用方法也极其简单。只需要继承一个CommandBase<AppSession, StringRequestInfo>类(注意:如果使用了自定义的Session,需要修改此处,如add类下的Add:CommandBase<MySession, StringRequestInfo>)类),并override这个类ExecuteCommand方法。现在我们来处理上篇文章的示例,先取消Telnet示例中的 appServer.NewRequestReceived 事件处理。这样我们就可以编写大量的命令让我们的Socket更灵活。
例如,我们可以定义一个名为"Hello "的类去处理Key为"Hello"的请求:
public class Hello: CommandBase<MySession, StringRequestInfo> { /// <summary> /// 自定义执行命令方法,注意传入的变量session类型为MySession /// </summary> /// <param name="session">会话</param> /// <param name="requestInfo">请求数据信息</param> public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo) { session.Send(string.Format("Hello {0}:{1} {2}", session.Config.Ip, session.Config.Port, requestInfo.Body)); } }
定义一个名为"ADD"的类去处理Key为"ADD"的请求:
public class ADD : CommandBase<MySession, StringRequestInfo> { public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo) { session.Send(requestInfo.Parameters.Select(p => Convert.ToInt32(p)).Sum().ToString()); } }
定义一个名为"MULT"的类去处理Key为"MULT"的请求:
public class MULT : CommandBase<MySession, StringRequestInfo> { public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo) { var result = 1; foreach (var factor in requestInfo.Parameters.Select(p => Convert.ToInt32(p))) { result *= factor; } session.Send(result.ToString()); } }
public class Echo: CommandBase<MySession, StringRequestInfo> { public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo) { session.Send(requestInfo.Body); } }
同时我们要移除请求处理方法的注册,因为它和命令不能同时被支持,注释下面代码即可
//appServer.NewRequestReceived += new RequestHandler<MySession, StringRequestInfo>(appServer_NewRequestReceived);
4、配置App.config使用BootStrap启动SuperSocket
SuperSocket配置section SuperSocket使用.NET自带的配置技术,SuperSocket有一个专门的配置Section.使用配置启动SuperSocket可以灵活配置选项
配置完成后,还需要修改program类。将原有在program中定义的端口信息以及方法注释,只保留服务启动和停止的代码。引入using SuperSocket.SocketEngine;使用BootStrap启动
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Protocol; using SuperSocket.SocketEngine; using SuperSocketDemo.Server; /**************************************************************** * 作者:黄昏前黎明后 * CLR版本:4.0.30319.42000 * 创建时间:2017-01-19 00:02:17 * 2017 * 描述说明:服务启动和停止入口 * * 修改历史: 2017 -01-19 调整自定义mysession和myserver * * *****************************************************************/ namespace SuperSocketDemo { class Program { /// <summary> /// SuperSocket服务启动或停止 /// </summary> /// <param name="args"></param> static void Main(string[] args) { Console.WriteLine("请按任何键进行启动SuperSocket服务!"); Console.ReadKey(); Console.WriteLine(); var bootstrap = BootstrapFactory.CreateBootstrap(); if (!bootstrap.Initialize()) { Console.WriteLine("初始化失败!"); Console.ReadKey(); return; } //修改appserver为myserver //var appServer = new AppServer(); // var appServer = new MyServer(); //注册事件 // appServer.NewSessionConnected += new SessionHandler<AppSession>(appServer_NewSessionConnected); //appServer.NewRequestReceived += new RequestHandler<AppSession, StringRequestInfo>(appServer_NewRequestReceived); //设置端口号 //int port = 2017; //启动应用服务端口 //if (!appServer.Setup(port)) //启动时监听端口2017 //{ // Console.WriteLine("服务端口启动失败!"); // Console.ReadKey(); // return; //} //Console.WriteLine(); ////尝试启动应用服务 //if (!appServer.Start()) //{ // Console.WriteLine("服务启动失败!"); // Console.ReadKey(); // return; //} var result = bootstrap.Start(); Console.WriteLine("服务正在启动: {0}!", result); if (result == StartResult.Failed) { Console.WriteLine("服务启动失败!"); Console.ReadKey(); return; } Console.WriteLine("服务启动成功,请按'E'停止服务!"); while (Console.ReadKey().KeyChar != 'E') { Console.WriteLine(); continue; } //停止服务 // appServer.Stop(); bootstrap.Stop(); Console.WriteLine("服务已停止!"); Console.ReadKey(); } /// <summary> /// 在事件处理代码中发送欢迎信息给客户端 /// </summary> /// <param name="session"></param> //static void appServer_NewSessionConnected(AppSession session) //{ // session.Send("Welcome to SuperSocket Telnet Server!"); //} /// <summary> ///客户端请求处理 /// </summary> /// <param name="session">会话</param> /// <param name="requestInfo">请求信息</param> //static void appServer_NewRequestReceived(AppSession session, StringRequestInfo requestInfo) //{ // switch (requestInfo.Key.ToUpper()) // { // case ("ECHO"): // session.Send(requestInfo.Body); // break; // case ("ADD"): // session.Send(requestInfo.Parameters.Select(p => Convert.ToInt32(p)).Sum().ToString()); // break; // case ("MULT"): // var result = 1; // foreach (var factor in requestInfo.Parameters.Select(p => Convert.ToInt32(p))) // { // result *= factor; // } // session.Send(result.ToString()); // break; // } //} } }
最后我们看一下修改后程序的运行结果:
断开调试工具看一下效果,可以看到服务端显示客户端断开连接
注意事项:
a) MyServer、自定义命令和MySession的访问权限必须设置为public
b) MyServer父类为AppServer<MySession>
c) MySession父类为AppSession<MySession>
d) HELLO父类为CommandBase<MySession,StringRequestInfo>,ExecueteCommand方法传入值类型分别为MySession和StringRequestInfo
e) 多服务器中需注意AppSession、AppServer、自定义命令中的AppSession不要搞错
调试常见错误:
总结:
通过自定义Session和Server,可以实现我们自己的AppSession和AppServer允许你根据你业务的需求来方便的扩展SuperSocket,你可以绑定session的连接和断开事件,服务器实例的启动和停止事件。你还可以在AppServer的Setup方法中读取你的自定义配置信息。总而言之,这些功能让你方便的创建一个你所需要的socket服务器成为可能。
小黎子,一个专注于数据分析整体数据仓库解决方案的程序猿!
作 者:黄昏前黎明后
出 处:http://www.cnblogs.com/fly-bird/
欢迎关注个人公众号:小黎子数据分析,转载文章请务必注明出处。