2013年3月9日

SuperSocket的研究(2)—— 实现自己的AppServer和AppSession

什么是AppSession和AppServer

AppSession代表一个逻辑Socket连接,基于连接的操作都应该在这个类中来定义。我们可以使用AppSession向顶层的客户端发送数据,从连接接收数据或者是关闭一个连接。
AppServer是Socket服务器的实例,它坚挺着所有客户端的连接,管理所有顶层客户端的连接。理想状况下,我们可以从AppServer中找到任意一个Session。应用级的操作和逻辑应该在这里定义。

创建自己的AppSession

  1. Super Socket的可扩展性较高,我们可以通过继承AppSession这个类并覆盖其中的方法来创建自己的AppSession(如下面的代码)。
  2. public class TelnetSession : AppSession<telnetsession>
    {
        protected override void OnSessionStarted()
        {
            this.Send("Welcome to SuperSocket Telnet Server");
        }
    
        protected override void HandleUnknownRequest(StringRequestInfo requestInfo)
        {
            this.Send("Unknow request");
        }
    
        protected override void HandleException(Exception e)
        {
            this.Send("Application error: {0}", e.Message);
        }
    
        protected override void OnSessionClosed(CloseReason reason)
        {
            //添加自己需要的业务逻辑,它们将在Session关闭后被执行
            base.OnSessionClosed(reason);
        }
    }
    ※注意:AppSession是一个抽象类,上述方法在父类中都是空方法,不进行任何操作。这里我们不难发现,TelnetSession继承了AppSession(我是第一次见到用泛型来继承的),覆盖了父类的4个方法。OnSessionStarted方法即是在连接建立之后服务器立即向客户端发送一条欢迎信息“Welcome to SuperSocket Telnet Server”。
  3. 可以genju yewuxuqiu 为你的Session添加新的属性,下面我们来创建一个奖杯用于游戏服务器的AppSession。
  4. public class PlayerSession :AppSession<PlayerSession>
    {
        public int GameHallId { get; internal set; }
    
        public int RoomId { get; internal set; }
    }
    
  5. 与Commands的关系
  6. 第一篇文章中,提到过commands,这里再来看看。
    public class ECHO : CommandBase<AppSession, StringRequestInfo>
    {
        public override void ExecuteCommand(AppSession session, StringRequestInfo requestInfo)
        {
            session.Send(requestInfo.Body);
        }
    }
    
    在上面的代码中,不难发现,ECHO的父类是带有泛型参数(AppSession)的CommandBase。这就是其默认的AppSession。如果你想使用自己新创建的AppSession,就需要传递你自己的AppSession的类型,否则服务器是不能发现此command:
    public class ECHO : CommandBase<PlayerSession, StringRequestInfo>
    {
        public override void ExecuteCommand(PlayerSession session, StringRequestInfo requestInfo)
        {
            session.Send(requestInfo.Body);
        }
    }
    

创建自己的AppServer

  1. 如果你想使用自己的AppSession,你必须修改你的AppServer。
  2. public class TelnetServer : AppServer<TelnetSession>
    {
    
    }
    
    简单说来,就是继承AppServer,并把自己定义的Session(继承AppSession)作为泛型参数。如上面代码所示,TelnetSession就可以在TelnetServer中被使用了。
  3. AppServer中有许多protected方法可供覆盖
  4. public class TelnetServer : AppServer<TelnetSession>
    {
        protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
        {
            return base.Setup(rootConfig, config);
        }
    
        protected override void OnStartup()
        {
            base.OnStartup();
        }
    
        protected override void OnStopped()
        {
            base.OnStopped();
        }
    }
    

好处及优点

实现自定义的AppSession和AppServer允许你按照自己的业务需求扩展SuperSocket。你可以监视(程序钩子)session的连接、关闭事件,server实例的启动和停止。你可以在AppServer的Setup()方法中读取自定义的配置。总而言之,它提供了大量用来构建你切实所需的自己的socket服务器的功能。

这次几乎又是纯翻译,其实作者已经发布了1.4的中文文档。我在这里翻译,主要是督促自己用心学习其中内容。如果我在完成其翻译后作者仍未发布1.5的中文文档,我希望能够在codeplex上更新中文文档,以尽绵薄之力。
本文中的代码我一行也没有自己调试过,觉得只要知道SuperSocket有这么回事就可以了但我一定写个小程序来试试文中的要点。我的学习方式受马士兵的影响很大,从这种学习项目的命名就可以看出。虽然我在看他的Struts2视频时,有些东西感觉不敢苟同,但这种学习方式认为还是可取的。

我已经写好了这个程序,并且调试通过了。我自定义了一个TelnetServer和TelnetSession,TelnetServer里并没有添加自己的逻辑,需要的话,一些日志记录可以放到这里。TelnetSession里添加了两个属性,覆盖了几个方法,例如覆盖了Send(string message)这个方法。创建了ECHO.cs以处理客户端的ECHO请求。以下是源码。

  1. TelnetServer.cs
  2. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using SuperSocket.SocketBase;
    using SuperSocket.SocketBase.Config;
    
    namespace SuperSocket_0200_AppServerAppSession
    {
        public class TelnetServer:AppServer<TelnetSession>
        {
            protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
            {
                return base.Setup(rootConfig, config);
            }
    
            protected override void OnStartup()
            {
                base.OnStartup();
            }
    
            protected override void OnStopped()
            {
                base.OnStopped();
            }
        }
    }
    
  3. TelnetSession.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using SuperSocket.SocketBase;
    
    namespace SuperSocket_0200_AppServerAppSession
    {
        public class TelnetSession:AppSession<TelnetSession>
        {
            public int UserId { get; internal set; }
    
            public string Username { get; internal set; }
            
            protected override void OnSessionStarted()
            {
                this.Send("Welcome to SuperSocket Server!");
                UserId = 1;
                Username = "test";
            }
    
            public override void Send(string message)
            {
                string prefix = Username + "(" + UserId + "):";
                base.Send(prefix+message);
            }
    
            protected override void HandleException(Exception e)
            {
                this.Send("Application error: {0}", e.Message);
            }
    
            protected override void OnSessionClosed(CloseReason reason)
            {
                //添加自己需要的业务逻辑,它们将在Session关闭后被执行
                base.OnSessionClosed(reason);
            }
    
        }
    }
    
  4. ECHO.cs
  5. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using SuperSocket.SocketBase.Command;
    using SuperSocket.SocketBase.Protocol;
    
    namespace SuperSocket_0200_AppServerAppSession
    {
        public class ECHO:CommandBase<TelnetSession,StringRequestInfo>
        {
            public override void ExecuteCommand(TelnetSession session, StringRequestInfo requestInfo)
            {
                session.Send(requestInfo.Body);
            }
        }
    }
    
  6. Program.cs
  7. using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace SuperSocket_0200_AppServerAppSession
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Press any key to start the server!");
                Console.ReadKey();
                Console.WriteLine();
                TelnetServer appSrv = new TelnetServer();
    
                //启动服务器
                if (!appSrv.Setup(2012))//启动服务器并监听2012端口
                {
                    Console.WriteLine("Failed to setup");
                    Console.ReadKey();
                    return;
                }
    
                Console.WriteLine();
    
                //Try to start the appServer
                if (!appSrv.Start())
                {
                    Console.WriteLine("Failed to start!");
                    Console.ReadKey();
                    return;
                }
    
                ////添加会话连接委托事件
                //appSrv.NewSessionConnected += new SessionHandler(appSrv_NewSessionConnected);
                ////添加收到请求后的委托事件
                //appSrv.NewRequestReceived += new RequestHandler(appSrv_NewRequestReceived);
    
                Console.WriteLine("The server started successfully, press key \"q\" to stop it. ");
                while (Console.ReadKey().KeyChar != 'q')
                {
                    Console.WriteLine();
                    continue;
                }
    
                //Stop the appServer
                appSrv.Stop();
                Console.WriteLine("The server was stopped");
                Console.ReadKey();
            }
    
        }
    }
    

没有评论:

发表评论