Unity游戏开发Photon Server之服务端架构
发表于2017-08-31
首先,服务端分两大部分,第一部分是服务端和客户端都需要使用的部分,第二部分就是接收客户端请求并发送数据的逻辑处理部分,也就是服务端架构了;
一、服务端和客户端通用部分
我们建立一个类库项目,将服务端和客户端需要进行通讯的数据类型存储在这里,包括操作代码(OperationCode),参数的增加和提取(ParameterTool.GetParameter和ParameterTool.AddParameter),以及参数的代码(ParameterCode)等。此外,数据库中的数据类型(DBModel(这里面的类是用来检索暂时存储数据库中的数据的))也放在这里。ParameterTool中的类如下:
public class ParameterTool { public static T GetParameter<T>(Dictionary<byte,object> parameters,ParameterCode parameterCode ,bool isObject = true) { object o = null; parameters.TryGetValue((byte) parameterCode, out o); if (isObject==false) { return (T) o; } return JsonMapper.ToObject<T>(o.ToString()); } public static void AddParameter<T>(Dictionary<byte, object> parameters, ParameterCode key, T value, bool isObject = true) { if (isObject) { string json = JsonMapper.ToJson(value); parameters.Add((byte) key, json); } else { parameters.Add((byte) key, value); } } public static SubCode GetSubcode(Dictionary<byte, object> parameters) { return GetParameter<SubCode>(parameters, ParameterCode.SubCode, false); } public static void AddSubcode(Dictionary<byte, object> parameters, SubCode subcode) { AddParameter<SubCode>(parameters,ParameterCode.SubCode, subcode,false); } public static void AddOperationcodeSubcodeRoleID(Dictionary<byte, object> parameters, OperationCode opCode, int roleID) { if(parameters.ContainsKey((byte) ParameterCode.OperationCode)) { parameters.Remove((byte) ParameterCode.OperationCode); } if(parameters.ContainsKey((byte) ParameterCode.RoleID)) { parameters.Remove((byte) ParameterCode.RoleID); } parameters.Add((byte)ParameterCode.OperationCode, opCode); parameters.Add((byte)ParameterCode.RoleID, roleID); } }
Model中是和数据库中表对应的类,除开一个工具类之外,其它的都是枚举类型。暂时所用到的结构就是这样。总结一下就是:Models,Tools,OperationOfCodes。
二、服务端架构
服务端有一个继承了ApplicationBase的ArpgApplication类,还有一个继承了PeerBase的ClientPeer.服务端接收到了连接的请求后会创建ClientPeer,接受Request和发送Response的函数都在ClientPeer中。
主要结构如下图:
此结构中所有的Handler与客户端的各个Controller对应,一个Controller对应一个Handler,他们之间处理相应的Request和Response.在服务端调用的时候,先是ClientPeer接收到Request,然后根据OperationCode,判断该调用哪个Handler,在ArpgApplication中找到对应的Handler,然后把Request发给它让它进行处理,返回处理的结果Response,发送给客户端。
在Handler处理的过程中,一般都会需要对数据库进行操作,所有的操作都封装在DB.Manager中,对相应的数据库操作调用相应的的Manager中的方法即可。
附上继承自ApplicationBase类的代码示例:
public class ARApplication : ApplicationBase { public List<ARPeer> peerListForTeam = new List<ARPeer>(); public Dictionary<byte, HandlerBase> handlers = new Dictionary<byte, HandlerBase>(); private static ARApplication _instance; private static readonly ILogger log = ExitGames.Logging.LogManager.GetCurrentClassLogger(); public ARApplication() { _instance = this; RegisterHandler(); } public static ARApplication AR_Instance { get { return _instance; } } void RegisterHandler() { //handlers.Add((byte)OperationCode.Login,new LoginHandler()); //handlers.Add((byte)OperationCode.MakeRoom,new MakeRoomHandler()); Type[] types = Assembly.GetAssembly(typeof(HandlerBase)).GetTypes(); //得到所有继承自HandlerBase的类 foreach (var type in types) { if (type.FullName.EndsWith("Handler")) { Activator.CreateInstance(type); //创建类的实例,为了能够使各个Handler的构造函数实现在ARApplication中注册 } } } protected override PeerBase CreatePeer(InitRequest initRequest) { return new ARPeer(initRequest.Protocol,initRequest.PhotonPeer); } protected override void Setup() { ExitGames.Logging.LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(this.ApplicationRootPath, "log"); GlobalContext.Properties["LogFileName"] = "MS" + this.ApplicationName; XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"))); log.Debug("ARServer is Setup!!!"); } protected override void TearDown() { log.Debug("ARServer is TearDown!!!"); } }
接着看继承自PeerBase类的实例:
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters) { HandlerBase handler; ARApplication.AR_Instance.handlers.TryGetValue(operationRequest.OperationCode, out handler); if (handler != null) { OperationResponse response = new OperationResponse(); response.OperationCode = operationRequest.OperationCode; response.Parameters = new Dictionary<byte, object>(); handler.OnHandlerMessage(operationRequest, response, this,sendParameters); SendOperationResponse(response, sendParameters); } else { //ARApplication的字典Handlers中没有operationRequest.OperationCode对应的Handler; } }
抽象的基类HandlerBase中的代码示例如下(各种Handler继承自HandlerBase并实现重写其中的抽象方法):
public abstract class HandlerBase { private static readonly ILogger log = ExitGames.Logging.LogManager.GetCurrentClassLogger(); public HandlerBase() { ARApplication.AR_Instance.handlers.Add((byte)opCode, this); //在Application类中注册各种Handler log.Debug("Hanlder:" + this.GetType().Name + " is register."); } public abstract void OnHandlerMessage(OperationRequest request,OperationResponse response, ARPeer peer, SendParameters sendParameters); public abstract OperationCode opCode { get; } }
大致就像下图: