码迷,mamicode.com
首页 > Web开发 > 详细

Scut:GameWebSocketHost 解析

时间:2016-08-16 13:14:03      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:

  想使用 Scut 做的是一个短连接项目,所以先直接看 GameWebSocketHost 了。

  先来看下 GameWebSocketHost 的成员:

protected bool EnableHttp;
public IActionDispatcher ActionDispatcher;
private EnvironmentSetting _setting;
private SocketListener socketListener;

  由之前的分析可知:SocketListener 搞定了监听、底层IO,那么ActionDispatcher 应该负责上层消息的分发了。

  构造函数做了各种参数准备、注册回调,不赘述。

 

  最为关键的是几个回调函数的具体实现:

 

1. GameSession 建立:

socketListener.Connected += new ConnectionEventHandler(socketLintener_OnConnectCompleted);
        private void socketLintener_OnConnectCompleted(ISocket sender, ConnectionEventArgs e)
        {
            try
            {
                var session = GameSession.CreateNew(e.Socket.HashCode, e.Socket, socketListener);  //最重要的是 GameSession 做什么用?
                session.HeartbeatTimeoutHandle += OnHeartbeatTimeout;
                OnConnectCompleted(sender, e);
            }
            catch (Exception err)
            {
                TraceLog.WriteError("ConnectCompleted error:{0}", err);
            }
        }

  GameSession:会话,这是每个连接在应用层的表示:

     session = new GameSession(keyCode, socket, appServer);

        private GameSession(Guid sid, ExSocket exSocket, ISocket appServer)
            : this(sid, null)
        {
            InitSocket(exSocket, appServer);
        }

        internal void InitSocket(ExSocket exSocket, ISocket appServer)
        {
            _exSocket = exSocket;  //GameSession 记录了这个连接所使用的 ExSocket 
    public class ExSocket
    {
     public Guid HashCode;           //哈希唯一标识
private Socket socket;          //管理了io套接字 private IPEndPoint remoteEndPoint; //管理了远程节点信息 private ConcurrentQueue<SocketAsyncResult> sendQueue; //管理通过该io套接字发送的消息队列 private int isInSending; internal DateTime LastAccessTime; public ExSocket(Socket socket) { HashCode = Guid.NewGuid(); sendQueue = new ConcurrentQueue<SocketAsyncResult>(); this.socket = socket; InitData(); } ... ... }

if (_exSocket != null) _remoteAddress = _exSocket.RemoteEndPoint.ToNotNullString();
            AppServer = appServer;  //还记录了所使用的 套接字监听器 
            if (User != null)
            {
                //update userid with sid.
                _userHash[UserId] = KeyCode;
            }
        }

  

  回顾一下 SocketListener 的代码:

    public class SocketListener : ISocket
    {
        public event ConnectionEventHandler Connected;
        private void OnConnected(ConnectionEventArgs e)  //当发生“成功连接”时,实际上就是调用了 
        {
            if (Connected != null)
            {
                Connected(this, e);
            }
        }
        ... ...
    }

    public class ConnectionEventArgs : EventArgs
    {
        public ExSocket Socket { get; set; }
        public DataMeaage Meaage { get; set; }
        ... ...
    }

  在 ProcessAccept 中:            

                    //之前已经成功创建了连接            
                SocketAsyncEventArgs ioEventArgs = this.ioEventArgsPool.Pop();
                    ioEventArgs.AcceptSocket = acceptEventArgs.AcceptSocket;
                    var dataToken = (DataToken)ioEventArgs.UserToken;
                    ioEventArgs.SetBuffer(dataToken.bufferOffset, socketSettings.BufferSize);
                    var exSocket = new ExSocket(ioEventArgs.AcceptSocket);   //将创建后的io套接字交给 ExSocket 管理
                    exSocket.LastAccessTime = DateTime.Now;
                    dataToken.Socket = exSocket;
                    acceptEventArgs.AcceptSocket = null;
                    ReleaseAccept(acceptEventArgs, false);
                    try
                    {
                        OnConnected(new ConnectionEventArgs { Socket = exSocket });  //在这里事实上调用了 socketLintener_OnConnectCompleted
            }                

 

2. 接收并处理消息

  在 SocketListener 中:

private void ProcessReceive(SocketAsyncEventArgs ioEventArgs)
{
      ... ...
      OnDataReceived(new ConnectionEventArgs { Socket = exSocket, Meaage = message });    
      ... ...
}

  OnDataReceived 就是 GameWebSocketHost 为自己管理的 SocketListener 所注册的 “数据接收” 处理API。

        private void OnDataReceived(ISocket sender, ConnectionEventArgs e)
        {
            try
            {
                RequestPackage package;
                if (!ActionDispatcher.TryDecodePackage(e, out package))   //在 EnvironmentSetting 的构造中可以看到 ActionDispatcher = new ScutActionDispatcher();  
                {                                   //同时将 ConnetctionEvenArgs 的 message 组装成 RequestPackage
                    //check command
                    string command = e.Meaage.Message;
                    if ("ping".Equals(command, StringComparison.OrdinalIgnoreCase))
                    {
                        OnPing(sender, e);
                        return;
                    }
                    if ("pong".Equals(command, StringComparison.OrdinalIgnoreCase))
                    {
                        OnPong(sender, e);
                        return;
                    }
                    OnError(sender, e);
                    return;
                }
                var session = GetSession(e, package);    //首次连接时已经建立了sesseion,此时直接获取即可
                if (CheckSpecialPackge(package, session)) //处理业务层的中断请求包、心跳请求包 
                {
                    return;
                }
                package.Bind(session);            //数据请求包绑定session,后面应该会需要从请求来获取session?
                ProcessPackage(package, session).Wait();   //处理具体的请求包

}
catch (Exception ex) { TraceLog.WriteError("Received to Host:{0} error:{1}", e.Socket.RemoteEndPoint, ex); } }

   

  ProcessPackage 是比较重要的API:

     private async System.Threading.Tasks.Task ProcessPackage(RequestPackage package, GameSession session)  //异步任务-多线程并发处理消息
        {
            if (package == null) return;

            try
            {
                ActionGetter actionGetter;
                byte[] data = new byte[0];
                if (!string.IsNullOrEmpty(package.RouteName))  //客户端通过本游戏对其他游戏进行远程调用
                {
                    actionGetter = ActionDispatcher.GetActionGetter(package, session);
                    if (CheckRemote(package.RouteName, actionGetter))
                    {
                        MessageStructure response = new MessageStructure();
                        OnCallRemote(package.RouteName, actionGetter, response);
                        data = response.PopBuffer();
                    }
                    else
                    {
                        return;
                    }
                }
                else   
                {
                    SocketGameResponse response = new SocketGameResponse();
                    response.WriteErrorCallback += ActionDispatcher.ResponseError;
                    actionGetter = ActionDispatcher.GetActionGetter(package, session);  //将 package 与 session 封装在一起
                    DoAction(actionGetter, response);                                    //利用本服务器的逻辑脚本处理模块处理消息
        protected void DoAction(ActionGetter actionGetter, BaseGameResponse response)
        {
            if (GameEnvironment.IsRunning && !ScriptEngines.IsCompiling)
            {
                OnRequested(actionGetter, response);
                ActionFactory.Request(actionGetter, response);    //Request 是如何操作的?
            }
            else
            {
                response.WriteError(actionGetter, Language.Instance.MaintainCode, Language.Instance.ServerMaintain);
            }
        }
                    data = response.ReadByte();
                }
                try
                {
                    if (session != null && data.Length > 0)
                    {
                        await session.SendAsync(actionGetter.OpCode, data, 0, data.Length, OnSendCompleted);
                    }
                }
                catch (Exception ex)
                {
                    TraceLog.WriteError("PostSend error:{0}", ex);
                }

            }
            catch (Exception ex)
            {
                TraceLog.WriteError("Task error:{0}", ex);
            }
            finally
            {
                if (session != null) session.ExitSession();
            }
        }

 

        public static void Request(ActionGetter actionGetter, BaseGameResponse response)
        {
            Request(GameEnvironment.Setting.ActionTypeName, actionGetter, response);
        }

        public static void Request(string typeName, ActionGetter actionGetter, BaseGameResponse response)
        {
            var actionId = actionGetter.GetActionId().ToInt();
            string tempName = string.Format(typeName, actionId);
            string errorInfo = "";
            try
            {
                bool isRL = BaseStruct.CheckRunloader(actionGetter);
                if (isRL || actionGetter.CheckSign())
                {
                    BaseStruct action = FindRoute(typeName, actionGetter, actionId);  //typeName 用于寻找消息处理模块,并返回一个基类为 BaseStruct 的实例
                    Process(action, actionGetter, response);                            //通过该句柄执行消息逻辑处理,并获取返回值,可见 BaseStruct 应该是更偏进业务层次的封装了
                    if (action != null)
                    {
                        return;
                    }
                }
                else
                {
                    errorInfo = Language.Instance.SignError;
                    TraceLog.WriteError("Action request {3} error:{2},rl:{0},param:{1}", isRL, actionGetter.ToString(), errorInfo, tempName);
                }
            }
            catch (Exception ex)
            {
                errorInfo = Language.Instance.ServerBusy;
                TraceLog.WriteError("Action request {0} error:{1}\r\nparam:{2}", tempName, ex, actionGetter.ToString());
            }
            response.WriteError(actionGetter, Language.Instance.ErrorCode, errorInfo);
        }

 

4. GameStruct 结构:

    public abstract class GameStruct
    {

        /// <summary>
        /// 默认的返回错误信息
        /// </summary>
        public const string DefaultErrorInfo = "Access fail";

        /// <summary>
        /// 接口访问处理情况
        /// </summary>
        public enum LogActionStat
        {
            /// <summary>
            /// 接口访问成功
            /// </summary>
            Sucess = 0,
            /// <summary>
            /// 访问失败
            /// </summary>
            Fail
        }
        /// <summary>
        /// 
        /// </summary>
        protected bool IsWebSocket = false;

        /// <summary>
        /// 
        /// </summary>
        protected Encoding encoding = Encoding.UTF8;
        /// <summary>
        /// 接口访问开始时间
        /// </summary>
        protected DateTime iVisitBeginTime;
        /// <summary>
        /// 接口访问结束时间
        /// </summary>
        protected DateTime iVisitEndTime;
        private string logActionResult = "";
        /// <summary>
        /// 
        /// </summary>
        protected ActionGetter actionGetter;

        /// <summary>
        /// 写日志的对象
        /// </summary>
        protected BaseLog oBaseLog = null;
        /// <summary>
        /// 数据类
        /// </summary>
        protected DataStruct dataStruct = new DataStruct();

        /// <summary>
        /// 当前游戏会话
        /// </summary>
        public GameSession Current { get; internal set; }

        /// <summary>
        /// 
        /// </summary>
        public int UserId
        {
            get
            {
                return Current != null ? Current.UserId : 0;
            }
        }
        /// <summary>
        /// ActionID,接口编号
        /// </summary>
        protected int actionId;

        /// <summary>
        /// 本次登录SessionID句柄
        /// </summary>
        protected string Sid;
        /// <summary>
        /// 是否是错误的URL请求串
        /// </summary>
        private bool IsError = false;

        /// <summary>
        /// 是否是主动推送
        /// </summary>
        protected bool IsPush = false;

        /// <summary>
        /// 是否影响输出, True:不响应
        /// </summary>
        protected bool IsNotRespond;

        /// <summary>
        /// 请求上来的消息编号,主动下发编号为0
        /// </summary>
        protected int MsgId = 0;

        /// <summary>
        /// 时间缀
        /// </summary>
        protected string St = "st";

        /// <summary>
        /// 返回Action是否为ErrorAction
        /// </summary>
        /// <returns></returns>
        public bool GetError()
        {
            return IsError;
        }
        private string errorInfo = string.Empty;
        /// <summary>
        /// 获取或设置错误信息
        /// </summary>
        public String ErrorInfo
        {
            get
            {
                return errorInfo;
            }
            set
            {
                errorInfo = value;
            }
        }

        private int errorCode = 0;
        /// <summary>
        /// 获取或设置错误信息
        /// </summary>
        public int ErrorCode
     ... ...
  }

  如果是 ActionGetter 是底层向业务层传递session与request的通道,GameStruct 则包含逻辑层向业务层反馈的参数。

  等具体跑起来再做细致分析罢。

Scut:GameWebSocketHost 解析

标签:

原文地址:http://www.cnblogs.com/Daniel-Liang/p/5770974.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!