在上一篇文章中,我们已经了解到了如何在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);
}
}
}

MySession类

    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("服务已停止");
}
}
}

MyServer类

    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));
}
}

Hello类

定义一个名为"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());
}
}

Add类

定义一个名为"MULT"的类去处理Key为"MULT"的请求:

public class MULT : CommandBase<MySession, StringRequestInfo>
{
public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo)
{
var result = ; foreach (var factor in requestInfo.Parameters.Select(p => Convert.ToInt32(p)))
{
result *= factor;
} session.Send(result.ToString());
}
}

Mult类

定义一个名为"Echo"的类去处理Key为"Echo"的请求:

public class Echo: CommandBase<MySession, StringRequestInfo>
{
public override void ExecuteCommand(MySession session, StringRequestInfo requestInfo)
{
session.Send(requestInfo.Body);
}
}

Echo类

同时我们要移除请求处理方法的注册,因为它和命令不能同时被支持,注释下面代码即可

//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;
// }
//}
}
}

program类

最后我们看一下修改后程序的运行结果:

断开调试工具看一下效果,可以看到服务端显示客户端断开连接

注意事项:

     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服务器成为可能。

SuperSocket入门(二)- 探索AppServer、AppSession,Conmmand和App.config的更多相关文章

  1. java虚拟机入门(二)-探索内存世界

    上节简单介绍了一下jvm的内存布局以及简单概念,那么对于虚拟机来说,它是怎么一步步的让我们能执行方法的呢: 1.首先,jvm启动时,跟个小领导一样会根据配置参数(没有配置的话jvm会有默认值)向大领导 ...

  2. SuperSocket入门(三)-Telnet多服务实例和服务实例交互配置详解

        在SuperSocket入门(二)中我们已经简单了解了通过配置App.config文件使用BootStrap启动SuperSocket服务.我们先来看一下上个案例中的基本配置文件示例: < ...

  3. SuperSocket基础二

    SuperSocket基础(二)-----一个完整的SocketServer项目 由于时间关系未能及时更新,关于SuperSocket,对于初学者而言,一个SuperSock的Server真的不好写. ...

  4. IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)

    1.系列文章引言 1.1 适合谁来阅读? 本系列文章尽量使用最浅显易懂的文字.图片来组织内容,力求通信技术零基础的人群也能看懂.但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获.如果您大 ...

  5. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

  6. [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?     http://www.52im.net/thread-1732-1-1.html   1.引言 本文接上篇<脑残式网 ...

  7. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  8. Entity Framework 程序设计入门二 对数据进行CRUD操作和查询

    前一篇文章介绍了应用LLBL Gen生成Entity Framework所需要的类型定义,用一行代码完成数据资料的读取, <LLBL Gen + Entity Framework 程序设计入门& ...

  9. Swift语法基础入门二(数组, 字典, 字符串)

    Swift语法基础入门二(数组, 字典, 字符串) 数组(有序数据的集) *格式 : [] / Int / Array() let 不可变数组 var 可变数组 注意: 不需要改变集合的时候创建不可变 ...

随机推荐

  1. javascript 回调, 单线程执行

    原文: http://www.cnblogs.com/aaronjs/p/3322466.html 这里的"回调"并不是"阻塞",而会空出执行线程,直至操作完成 ...

  2. @media max-width 与jquery宽度取值的差异

    最近写了个响应式网站,有些效果通过用jq的$(window).width()来判断屏幕宽度,当屏幕宽度小于1366时一些参数发生变化.@media中也有小于1366的判断条件,但是用起来的时候发现一个 ...

  3. ios开发证书,描述文件,bundle ID的关系

    苹果为了控制应用的开发与发布流程,制定了一套非常复杂的机制.这里面的关键词有:个人开发者账号,企业开发者账号,bundle ID,开发证书,发布证书(又叫"生产证书"),开发描述文 ...

  4. The 2014 ACMICPC Asia Regional Xian Online

    [A]签到题 [B]后缀数组 [C]染色,DP(感觉可出) [D]BFS搜索,有点麻烦 [E]博弈论,Nim博弈 [F]BFS状态搜索 [G]概率DP+状态压缩 [H]异或+构造 [I]矩阵快速幂(队 ...

  5. bzoj3322 最大生成树+LCA

    题目大意:给个无向图,每条边有个限制,每个点最多能买入和卖出一定黄金:然后按顺序走过n个点,求每个卖出黄金的点最多能卖出多少黄金 一开始有点懵,想着怎么再图上做这个问题,后来知道要先建一棵最大生成树 ...

  6. 加载window事件

    $(document).ready()和window.onload的区别 发表于 2012-08-29 由 admin 以浏览器装载文档为例,在页面加载完毕后,浏览器会通过JavaScript为DOM ...

  7. OpenStack - liberty CentOS 7

    OpenStack私有云部署 Controller Node:       em1(10.6.17.11),em2() Computer Node:         em1(10.6.17.12),e ...

  8. WKWebKit基础

    UIWebView & UIWebViewDelegate 这个两个东西在 WKWebKit 中被重构成 14 个类 3 个协议. WKWebKit Framework Classes WKB ...

  9. 《玩转Bootstrap(基础)》笔记

    基本的HTML模板 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  10. iOS开发——浅谈构架与用户体验

    工作不是千篇一律的重复,从中寻找乐趣才是我们应该做的. 作为一名码农,做过几个项目,每次做项目的时候都会自己构思,如果完全是我自己设计,会怎么去设计?心里一直没有满意的答案,不管怎么布局,好像都感觉差 ...