标签:
想使用 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 则包含逻辑层向业务层反馈的参数。
等具体跑起来再做细致分析罢。
标签:
原文地址:http://www.cnblogs.com/Daniel-Liang/p/5770974.html