SuperSocket入門(二)- 探索AppServer、AppSession,Conmmand和App.config

      在上一篇文章中,咱們已經瞭解到瞭如何在SuperSocket處理客戶端請求。 同時咱們可能會發現一個問題,若是咱們的服務器端包含有不少複雜的業務邏輯,這樣的switch/case代碼將會很長並且很是難看,而且沒有遵循面向對象設計的原則(OOD)。 在這種狀況下,SuperSocket提供了一些讓咱們在多個獨立的類中處理各自不一樣的請求的命令框架,接下來咱們一塊兒來看一下怎麼使用
    一、自定義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類
    二、自定義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"的請求:bootstrap

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 = 1;

        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類

    同時咱們要移除請求處理方法的註冊,由於它和命令不能同時被支持,註釋下面代碼便可session

    //appServer.NewRequestReceived += new RequestHandler<MySession, StringRequestInfo>(appServer_NewRequestReceived);app

   四、配置App.config使用BootStrap啓動SuperSocket框架

   SuperSocket配置section SuperSocket使用.NET自帶的配置技術,SuperSocket有一個專門的配置Section.使用配置啓動SuperSocket能夠靈活配置選項socket

     配置完成後,還須要修改program類。將原有在program中定義的端口信息以及方法註釋,只保留服務啓動和中止的代碼。引入using SuperSocket.SocketEngine;使用BootStrap啓動ide

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類

    最後咱們看一下修改後程序的運行結果:工具

斷開調試工具看一下效果,能夠看到服務端顯示客戶端斷開鏈接this

注意事項:spa

     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服務器成爲可能。

相關文章
相關標籤/搜索