本文继PhotonServer(四)Unity连接服务器实现登陆、注册(上)之后进行讲解服务器端的相关配置。
服务器端脚本部署
首先我们在服务器创建一个文件夹Handler,这个文件夹下的文件都用来接收处理客户端发来的消息。
在Handler文件夹下创建相应的脚本IHandlerBase、LoginHandler
创建Net文件夹,并将与网络相关的ClientPeer、MyGameServer脚本拖入进去
在Net文件夹创建脚本HandlerMediat、OperationCode
创建Tools文件夹,并将NhibernateHelper脚本拖入进去
在Tools文件夹创建脚本DictTool
一、接口IHandlerBase 作为接收客户端消息类的基类
二、LoginHandler类,该类用来接收客户端发送的登录、注册消息的处理
三、HandlerMediat类,该类与Unity客户端的类似,用于分发接收到客户端的消息
四、HandlerMediat类对应的枚举OperationCode,该枚举值是记录该消息是属于什么类型,比如用户登录消息、用户注册消息、获取背包消息等等
五、DictTool类,该类是做字典解析的封装
六、修改MyGameServer脚本,添加一个ReturnCode枚举值,对应消息成功失败。在Setup和TearDown方法里分别添加Handler和移除Handler
七、修改ClientPeer脚本,在OnOperationRequest方法里分发消息
IHandlerBase
1 2 3 4 5 6 7 8 9 |
namespace MyGameServer.Handler { interface IHandlerBase { void AddListener(); void RemoveListener(); } } |
LoginHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
using MyGameServer.Manager; using MyGameServer.Model; using MyGameServer.Tools; using Photon.SocketServer; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyGameServer.Handler { //处理登陆请求的类 class LoginHandler : IHandlerBase { // 添加监听 public void AddListener() { HandlerMediat.AddListener(OperationCode.Login, OnLoginReceived); HandlerMediat.AddListener(OperationCode.Register, OnRegisterReceived); } // 移除监听 public void RemoveListener() { HandlerMediat.RemoveListener(OperationCode.Login, OnLoginReceived); HandlerMediat.RemoveListener(OperationCode.Register, OnRegisterReceived); } // 登陆请求的处理的代码 void OnLoginReceived(ClientPeer peer, OperationRequest operationRequest, SendParameters sendParameters) { //根据发送过来的请求获得用户名和密码 string username = DictTool.GetValue<byte, object>(operationRequest.Parameters, 1) as string; string password = DictTool.GetValue<byte, object>(operationRequest.Parameters, 2) as string; //连接数据库进行校验 UserManager manager = new UserManager(); bool isSuccess = manager.VerifyUser(username, password); OperationResponse response = new OperationResponse(operationRequest.OperationCode); //如果验证成功,把成功的结果利用response.ReturnCode返回成功给客户端 if (isSuccess) { response.ReturnCode = (short)ReturnCode.Success; } else//否则返回失败给客户端 { response.ReturnCode = (short)ReturnCode.Failed; } //把上面的回应给客户端 peer.SendOperationResponse(response, sendParameters); } // 注册请求的处理的代码 void OnRegisterReceived(ClientPeer peer, OperationRequest operationRequest, SendParameters sendParameters) { string username = DictTool.GetValue<byte, object>(operationRequest.Parameters, 1) as string; string password = DictTool.GetValue<byte, object>(operationRequest.Parameters, 2) as string; UserManager manager = new UserManager(); User user = manager.GetByUsername(username);//根据username查询数据 OperationResponse responser = new OperationResponse(operationRequest.OperationCode); //如果没有查询到代表这个用户没被注册过可用 if (user == null) { //添加输入的用户和密码进数据库 user = new User() { Username = username, Password = password }; manager.Add(user); responser.ReturnCode = (short)ReturnCode.Success;//返回成功 } else//否者这个用户被注册了 { responser.ReturnCode = (short)ReturnCode.Failed;//返回失败 } // 把上面的结果给客户端 peer.SendOperationResponse(responser, sendParameters); } } } |
HandlerMediat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
using System.Collections.Generic; using System; using Photon.SocketServer; public class HandlerMediat { public delegate void Act(MyGameServer.ClientPeer peer, OperationRequest operationRequest, SendParameters sendParameters); static Dictionary<OperationCode, Delegate> messageTable = new Dictionary<OperationCode, Delegate>(); public static void AddListener(OperationCode type, Act act) { if (!messageTable.ContainsKey(type)) { messageTable.Add(type, null); } Delegate d = messageTable[type]; if (d != null && d.GetType() != act.GetType()) { MyGameServer.MyGameServer.log.Info(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", type, d.GetType().Name, act.GetType().Name)); } else { messageTable[type] = (Act)messageTable[type] + act; } } public static void RemoveListener(OperationCode type, Act act) { if (messageTable.ContainsKey(type)) { Delegate d = messageTable[type]; if (d == null) { MyGameServer.MyGameServer.log.Info(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", type)); } else if (d.GetType() != act.GetType()) { MyGameServer.MyGameServer.log.Info(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", type, d.GetType().Name, act.GetType().Name)); } else { messageTable[type] = (Act)messageTable[type] - act; if (d == null) { messageTable.Remove(type); } } } else { MyGameServer.MyGameServer.log.Info(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", type)); } } public static void Dispatch(OperationCode type, MyGameServer.ClientPeer peer, OperationRequest operationRequest, SendParameters sendParameters) { Delegate d; if (messageTable.TryGetValue(type, out d)) { Act callback = d as Act; if (callback != null) { callback(peer, operationRequest, sendParameters); } else { MyGameServer.MyGameServer.log.Info(string.Format("no such event type {0}", type)); } } } public static void RemoveAllListener(OperationCode type) { if (messageTable.ContainsKey(type)) { messageTable[type] = null; messageTable.Remove(type); } else { MyGameServer.MyGameServer.log.Info(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", type)); } } } |
OperationCode
1 2 3 4 5 6 |
public enum OperationCode : byte { Login, Register, Default } |
DictTool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyGameServer.Tools { public class DictTool { //扩展方法 public static Value GetValue<Key, Value>(Dictionary<Key, Value> dicr, Key key) { Value value; bool isSuccess = dicr.TryGetValue(key, out value); if (isSuccess) { return value; } else { return default(Value); } } } } |
MyGameServer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Photon.SocketServer; using ExitGames.Logging; using System.IO; using ExitGames.Logging.Log4Net; using log4net.Config; using MyGameServer.Manager; using MyGameServer.Model; using MyGameServer.Handler; namespace MyGameServer { public enum ReturnCode { Success = 0, Failed = -1 } // 所有的Server端 主类都要继承自applicationbase public class MyGameServer : ApplicationBase { public static readonly ILogger log = LogManager.GetCurrentClassLogger(); public static MyGameServer Instance { get; private set; } // 当一个客户端请求连接的时候,服务器端就会调用这个方法 // 我们使用peerbase,表示和一个客户端的链接,然后photon就会把这些链接管理起来 protected override PeerBase CreatePeer(InitRequest initRequest) { log.Info("一个客户端连接进来了!"); return new ClientPeer(initRequest); } //初始化(当整个服务器启动起来的时候调用这个初始化) protected override void Setup() { Instance = this; // 日志的初始化(定义配置文件log4net位置) // Path.Combine 表示连接目录和文件名,可以屏蔽平台的差异 // Photon: ApplicationLogPath 就是配置文件里面路径定义的属性 //this.ApplicationPath 表示可以获取photon的根目录,就是Photon-OnPremise-Server-SDK_v4-0-29-11263\deploy这个目录 // 这一步是设置日志输出的文档文件的位置,这里我们把文档放在Photon-OnPremise-Server-SDK_v4-0-29-11263\deploy\bin_Win64\log里面 log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(Path.Combine(Path.Combine(this.ApplicationRootPath, "bin_win64")), "log"); //this.BinaryPath表示可以获取的部署目录就是目录Photon-OnPremise-Server-SDK_v4-0-29-11263\deploy\MyGameServer\bin FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));// 告诉log4net日志的配置文件的位置 // 如果这个配置文件存在 if (configFileInfo.Exists) { LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance);// 设置photon我们使用哪个日志插件 XmlConfigurator.ConfigureAndWatch(configFileInfo);// 让log4net这个插件读取配置文件 } log.Info("Setup Completed!");// 最后利用log对象就可以输出了 AddHandler(); } // server端关闭的时候 protected override void TearDown() { RemoveHandler(); } #region 所有Handler LoginHandler loginHandler; //BagHandler bagHandler; //EquipmentHandler equipmentHandler; #endregion // 初始化所有Handler public void AddHandler() { loginHandler = new LoginHandler(); loginHandler.AddListener(); } // 移除所有Handler void RemoveHandler() { loginHandler.RemoveListener(); } } } |
ClientPeer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Photon.SocketServer; using PhotonHostRuntimeInterfaces; using MyGameServer.Handler; namespace MyGameServer { //管理跟客户端的链接的 public class ClientPeer : Photon.SocketServer.ClientPeer { public ClientPeer(InitRequest initRequest) : base(initRequest) { } //处理客户端断开连接的后续工作 protected override void OnDisconnect(DisconnectReason reasonCode, string reasonDetail) { } //处理客户端的请求 protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters) { //OperationRequest封装了请求的信息 //SendParameters 参数,传递的数据 HandlerMediat.Dispatch((OperationCode)operationRequest.OperationCode, this, operationRequest, sendParameters); // 分发消息 } } } |
测试登录、注册功能
服务器配置完成后,重新生成下MyGameServer,再重启服务器,就可以打开Unity验证下登录和注册的功能
首先数据库里原本数据内容如下:
现在我们验证注册功能:
验证登录功能:
客户端及服务器源码已分享至网盘:
链接: https://pan.baidu.com/s/1i51n5e1 密码: cky3
- 本文固定链接: http://www.u3d8.com/?p=1466
- 转载请注明: 网虫虫 在 u3d8.com 发表过
你好我照着你的登录和注册文章写得photonSever服务器和unity客户端,Unity发布后自己家里电脑可以登录,但是同学家的外网却不能登录请问这是什么原因呢
一般家里电脑都不能被外网直接访问的,要看是不是有外网独立ip 还要在路由设置端口映射 比较麻烦的。在企业里,运维部门会帮忙处理好这些的。