Unity游戏开发Photon Server之服务端架构
首先,服务端分两大部分,第一部分是服务端和客户端都需要使用的部分,第二部分就是接收客户端请求并发送数据的逻辑处理部分,也就是服务端架构了;
一.服务端和客户端通用部分
我们建立一个类库项目,将服务端和客户端需要进行通讯的数据类型存储在这里,包括操作代码(OperationCode),参数的增加和提取(ParameterTool.GetParameter<T>和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;
}
}
大致就像下图: