by 草帽java
這節咱們繼續上節所講的內容,上節咱們初始化好了LoginWindow,當咱們點擊確認選擇服務器按鈕的時候,就發送服務器id給遊戲服務器。web
這裏就開始涉及到客戶端須要跟服務器的網絡通訊,因此咱們得先來寫個客戶端的Socket框架:服務器
NetworkManager.cs單例腳原本管理。網絡
由於我以前作過了這個腳本,因此我特地封裝了本身的網路管理network.dll,直接就能夠拿來用。框架
至於這裏面的腳本,有興趣的童鞋能夠本身去反編譯下看看。ide
這個dll下載完成以後,咱們新建一個文件夾,取名爲Plugin,而後把這個dll拖到裏面去。ui
接着,開始正式的編寫NetworkManager腳本:this
using UnityEngine; using System.Collections; using System; using Game; public class NetworkManager : Singleton<NetworkManager> { private WWW m_wwwRequest = null; private float m_fLastTimeForHeartBeat = 0f; public static string serverURL = "http://127.0.0.1/LOLGameDemo/serverUrl.xml"; public static string serverIP = "127.0.0.1"; public static int serverPort = 8000; public CNetProcessor m_netProcessor = null; public CNetwork m_network = null; private IXLog m_log = XLog.GetLog<NetworkManager>(); public SocketState NetState { get { SocketState result; if (null != this.m_network) { SocketState state = this.m_network.GetState(); result = state; } else { result = SocketState.State_Closed; } return result; } } public void Init() { CRegister.RegistProtocol(); CNetObserver cNetObserver = new CNetObserver(); this.m_netProcessor = new CNetProcessor(); this.m_netProcessor.Observer = cNetObserver; cNetObserver.oProc = this.m_netProcessor; this.m_network = new CNetwork(); CPacketBreaker oBreaker = new CPacketBreaker(); string newFullPath = SystemConfig.ResourceFolder; if (!this.m_network.Init(this.m_netProcessor, oBreaker, 65536u, 65536u, newFullPath, true)) { this.m_log.Fatal("oNet.Init Error"); } else { this.m_log.Debug("oNet.Init Success!"); this.m_netProcessor.Network = this.m_network; } } public void FixedUpdate() { try { if (this.m_netProcessor != null && null != this.m_netProcessor.Network) { this.m_netProcessor.Network.ProcessMsg(); //this.m_netProcessor.Network.CheckHeartBeat(CommonDefine.IsMobilePlatform); } } catch (Exception ex) { this.m_log.Fatal(ex.ToString()); } } private IEnumerator ConnectToURL() { Debug.Log("ConnectToURL:" + NetworkManager.serverURL); this.m_wwwRequest = new WWW(NetworkManager.serverURL); bool flag = false; for (int i = 0; i < 10; i++) { yield return new WaitForSeconds(0.5f); if (this.m_wwwRequest.isDone) { if (string.IsNullOrEmpty(this.m_wwwRequest.error)) { if (!string.IsNullOrEmpty(this.m_wwwRequest.text)) { string[] array = this.m_wwwRequest.text.Split(new char[] { ':' }); if (array.Length == 2) { NetworkManager.serverIP = array[0];//遊戲網關服務器ip地址 NetworkManager.serverPort = Convert.ToInt32(array[1]); flag = true; this.RealConnectToServer(); } else { Debug.LogError("m_wwwRequest.text=:" + this.m_wwwRequest.text); } } } else { Debug.LogError(this.m_wwwRequest.error.ToString()); } break; } } if (null != this.m_wwwRequest) { this.m_wwwRequest.Dispose(); this.m_wwwRequest = null; } if (!flag) { /*if (Singleton<Login>.singleton.IsLogined) { Singleton<ReConnect>.singleton.OnConnectToURLFailed(); } else { Singleton<Login>.singleton.OnConnectToURLFailed(); }*/ } yield break; } private void RealConnectToServer() { this.m_log.Info(string.Format("ConnectToServer:{0}:{1}", NetworkManager.serverIP, NetworkManager.serverPort)); if (!this.m_netProcessor.Network.Connect(NetworkManager.serverIP, NetworkManager.serverPort)) { Debug.Log("Failed"); /*if (!Singleton<Login>.singleton.IsLogined) { Singleton<Login>.singleton.OnConnectFailed(); } else { Singleton<ReConnect>.singleton.OnConnectFailed(); }*/ } else { this.m_log.Info("Connecting..."); } } public void ConnectToServer() { if (string.IsNullOrEmpty(NetworkManager.serverURL)) { this.m_log.Error("serverURL IsNullOrEmpty"); } LOLGameDriver.Instance.StartCoroutine(this.ConnectToURL()); } public void SendMsg(CProtocol ptc) { try { if (null != this.m_netProcessor) { this.m_netProcessor.Send(ptc); } } catch (Exception ex) { this.m_log.Fatal(ex.ToString()); } } public void Close() { Debug.Log("NetWorkManager::Close"); if (null != this.m_network) { this.m_network.Close(); } if (null != this.m_wwwRequest) { this.m_wwwRequest.Dispose(); this.m_wwwRequest = null; } } public void UnInit() { Debug.Log("NetWorkManager::OnApplicationQuit"); if (null != this.m_network) { this.m_network.UnInit(); this.m_network = null; } this.m_netProcessor = null; } /// <summary> /// 發送登錄請求 /// </summary> /// <param name="username"></param> /// <param name="password"></param> /// <param name="serverId"></param> public void SendLogin(string username, string password, int serverId) { CptcC2GReq_Login instance = new CptcC2GReq_Login() { m_strUsername = username, m_strPassword = password, m_dwServerId = serverId }; SendMsg(instance); } }
由於客戶端和服務器是有協議約定的,因此咱們每條消息都必須準守這個協議,這裏我在network.dll裏面就搞了個協議抽象基類Cptrocol.cs,而後本身在Unity中新建CRegister.cs來管理這些消息。阿里雲
CRegister:
using UnityEngine; using System.Collections; namespace Game { internal class CRegister { /// <summary> /// 註冊消息協議 /// </summary> public static void RegistProtocol() { CProtocol.Register(new CptcG2CNtf_LoginResult());//1001,註冊登陸消息 } } }
能夠有些童鞋還會有報錯,這裏咱們得在寫個類:CNetObserver:主要用於接收消息的監控
using UnityEngine; using System.Collections; using Utility; namespace Game { public class CNetObserver : INetObserver { public CNetProcessor oProc = null; public void OnConnect(bool bSuccess) { if (!bSuccess) { } else { XLog.Log.Debug("Connect Success!"); } } public void OnClosed(NetErrCode nErrCode) { XLog.Log.Error(string.Format("OnClosed:{0}", nErrCode.ToString())); } public void OnReceive(int unType, int nLen) { Singleton<PerformanceAnalyzer>.singleton.OnRecevie((uint)nLen); } public void OnSend(int dwType, int nLen) { Singleton<PerformanceAnalyzer>.singleton.OnSend((uint)nLen); } } }
OK,這樣大體的網絡通訊的單例腳本就寫完了。這裏我講下大體的鏈接步驟流程。
首先,咱們點擊確認選擇按鈕,先判斷是否已經鏈接上網關服務器,若是沒有就開始鏈接。
那麼怎麼個鏈接法,我這裏採用的是,先訪問web站點,從站點中獲取網關服務器ip地址和端口號。這樣的好處是能動態的登錄網關服務器,好比網關1崩潰了,我只要改下web站點的ip就能夠了,若是在程序內寫死的話,這樣又得從新更新客戶端,麻煩。
你們能夠看到我這邊寫的是我本身的web站點地址,這裏,大家得改爲大家本身寫的。
這裏我寫的ip地址是127.0.0.1,端口號8000,由於咱們就在本地測試,因此就寫本地ip,若是是騰訊或者阿里雲的話,咱們就改爲他的ip地址便可。
而後咱們回到LoginCtrl裏面,添加一個方法LoginStart,傳遞的參數是選擇的服務器id:
public void LoginStart(int serverId) { //若是已經鏈接上,就直接發送登錄消息請求 if (Singleton<NetworkManager>.singleton.NetState == SocketState.State_Connected) { SystemConfig.SelectedServerIndex = serverId;//保存上次選擇的服務器id SystemConfig.LocalSetting.SelectedServer = serverId;//保存到本地配置 //發送登錄消息請求 NetworkManager.singleton.SendLogin(this.username,this.password,serverId); } else { Singleton<NetworkManager>.singleton.ConnectToServer();//開始鏈接服務器 } }
而後把這個方法添加到LoginWindow的SelectServerButton當作事件監聽。
/// <summary> /// 登錄遊戲服務器 /// </summary> /// <param name="go"></param> public void OnLoginServer(GameObject go) { if (this.m_selectedServerId >= 0) { LoginCtrl.singleton.LoginStart(this.m_selectedServerId); } else { Debug.LogError("選擇服務器不存在"); } }
OK,咱們回到NetworkManager中,來寫SendLogin這方法,主要是發送登錄消息給遊戲網關服務器。
/// <summary> /// 發送登錄請求 /// </summary> /// <param name="username"></param> /// <param name="password"></param> /// <param name="serverId"></param> public void SendLogin(string username, string password, int serverId) { CptcC2GReq_Login instance = new CptcC2GReq_Login() { m_strUsername = username, m_strPassword = password, m_dwServerId = serverId }; SendMsg(instance); }
CptcC2GReq_Login這條就是登錄消息,遵照CProtocol
using UnityEngine; using System.Collections; using Game; /// <summary> /// 客戶端發送給服務器的登錄消息請求 /// </summary> public class CptcC2GReq_Login : CProtocol { public string m_strUsername;//用戶名 public string m_strPassword;//密碼 public int m_dwServerId;//選擇的服務器id public CptcC2GReq_Login() : base(1000) { this.m_strUsername = ""; this.m_strPassword = ""; this.m_dwServerId = 0; } public override CByteStream DeSerialize(CByteStream bs) { bs.Read(ref m_strUsername); bs.Read(ref m_strPassword); bs.Read(ref m_dwServerId); return bs; } public override CByteStream Serialize(CByteStream bs) { bs.Write(this.m_strUsername); bs.Write(this.m_strPassword); bs.Write(this.m_dwServerId); return bs; } public override void Process() { } }
可見這條消息包括3個須要傳遞的變量:
public string m_strUsername;//用戶名 public string m_strPassword;//密碼 public int m_dwServerId;//選擇的服務器i
有用戶名、密碼、還有選擇的遊戲服務器id。
OK,客戶端的通訊已經完成,接着咱們就得開始搭建服務器,我這裏採用的是java的mina框架。
不懂的童鞋能夠去先了解一下,很快就能入門。
CptcC2GReq_Login