根據對NetWorkServer 以及NetworkClient的理解,編寫一個簡易版的NetWork Manager。慣例所有代碼放在最後編程
(一)NetWorkServer與NetworkClient機制網絡
網絡中一個Server對應對各client,unet NetWorkServer主要爲靜態方法,經過綁定ip地址以及端口就能夠監聽鏈接,client經過connect進行鏈接,當鏈接成功後經過註冊的相關消息進行鏈接成功與否的方法進行相應處理(採用socket編程時通常經過回調倆進行處理,在unet中稍有不一樣,但原理同樣)socket
1)server進行監聽,並註冊監聽事件測試
代碼以下:this
NetworkServer.Listen(ip, port); NetworkServer.RegisterHandler(MsgType.Connect, OnServerConnect); NetworkServer.RegisterHandler(MsgType.Disconnect, OnServerDisconnect);//若是stopServer則不會觸發此事件,只有客戶端主動斷開鏈接(stopclient)時觸發 NetworkServer.RegisterHandler(MsgType.AddPlayer, OnServerAddPlayer); NetworkServer.RegisterHandler(MsgType.RemovePlayer, OnServerRemovePlayer); NetworkServer.RegisterHandler(CustomerMsgType.StringMsg, OnServerReceiveStringMsg);
上述RegisterHandler註冊的監聽事件中前四個爲unet自定義消息類型,以MsgType.Connect爲例,根據變量名字可知當server在MsgType.Connect註冊OnServerConnect方法,當client端鏈接到server端時,server端會調用OnServerConnect方法。unet容許自定義消息類型,如NetworkServer.RegisterHandler(CustomerMsgType.StringMsg, OnServerReceiveStringMsg); 自定義消息類型CustomerMsgType.StringMsg(short類型)不能與unet自定義的消息類型衝突。client端要註冊相對應的消息類型spa
2)client進行鏈接,並註冊事件code
unetClient = new NetworkClient(); RegisterClientMessage(unetClient); unetClient.Connect(ip, port);
void RegisterClientMessage(NetworkClient client)
{
client.RegisterHandler(MsgType.Connect, OnClientConnect);
client.RegisterHandler(MsgType.Disconnect, OnClientDisconnect);//服務端主動斷開或者stopServer時觸發,但客戶端主動斷開鏈接(stopClient)時不會觸發
client.RegisterHandler(CustomerMsgType.StringMsg, OnClientReceiveStringMsg);
if (localPlayer!=null)
{
ClientScene.RegisterPrefab(localPlayer);
}
}
(二)clientScene場景管理server
經過上一步,實現了Server與client的鏈接,一個client與server鏈接成功後對應一個networkconnection,經過鏈接綁定相應的遊戲物體來進行操做則須要clientScene。clientScene組要爲靜態方法,統一管理鏈接的遊戲物體。blog
1)當客戶端接收到鏈接成功後在OnClientConnect建立遊戲物體(能夠本身手動代碼建立,可是unet已經爲咱們組好處理,直接調用便可)繼承
NetworkConnection conn = msg.conn; ClientScene.Ready(conn); CustomerMsgType.StringMessage stringMsg = new CustomerMsgType.StringMessage(); stringMsg.stringMsg = "ttt"; ClientScene.AddPlayer(conn, (short)conn.connectionId, stringMsg);
2)鏈接成功後經過clientScene添加玩家(AddPlayer),調用後會激發服務端註冊的OnServerAddPlayer方法( NetworkServer.RegisterHandler(MsgType.AddPlayer, OnServerAddPlayer);)而後生成服務端遊戲物體
void OnServerAddPlayer(NetworkMessage msg) { //var message = msg.ReadMessage<CustomerMsgType.StringMessage>(); //Debug.Log(message.stringMsg); var player = Instantiate(localPlayer); msg.ReadMessage(addPlayerMsg); //Debug.Log(Encoding.Default.GetString(addPlayerMsg.msgData)); NetworkServer.AddPlayerForConnection(msg.conn, player, addPlayerMsg.playerControllerId); LogInfo.theLogger.Log("Client "+msg.conn.connectionId+" is added"); }
(三)前兩步梳理
1)服務端開啓監聽
2)客戶端鏈接
3)鏈接後客戶端向clientscene添加玩家
4)服務端接收到玩家添加成功的消息,並建立玩家
(四)消息發送
客戶端服務端發送消息時,要先註冊消息類型以及對應的方法,經過NetworkClient的send方法以及NetworkServer的send方進行發送。
1)註冊消息
如步驟(一)中的client.RegisterHandler(CustomerMsgType.StringMsg, OnClientReceiveStringMsg)(客戶端) NetworkServer.RegisterHandler(CustomerMsgType.StringMsg, OnServerReceiveStringMsg)(服務端)
2)消息的發送
public void Send(string msg) { CustomerMsgType.StringMessage stringMsg = new global::CustomerMsgType.StringMessage(); stringMsg.stringMsg = msg; unetClient.Send(CustomerMsgType.StringMsg, stringMsg); }
向服務端發送消息,發送時包含發送的消息主體stringMsg(爲繼承MessageBase的類型)以及發送消息的類型CustomerMsgType.StringMsg
3)經過NetworkMessage進行消息讀取
全部註冊的消息對應的方法都含有一個NetworkMessage參數,它包含這次操做 對應的networkconnection以及傳遞過來的消息,消息的讀取以下所示
void OnServerReceiveStringMsg(NetworkMessage msg) { var receivedMsg = msg.ReadMessage<CustomerMsgType.StringMessage>(); //var newMsg = "Server received:" + receivedMsg.stringMsg; LogInfo.theLogger.Log(receivedMsg.stringMsg); var message = new CustomerMsgType.StringMessage(); message.stringMsg = "Server received:" + receivedMsg.stringMsg ; NetworkServer.SendToClient(lastClient.connectionId, CustomerMsgType.StringMsg, message); }
服務端端向客端發送時,除了要輸入發送的類型CustomerMsgType.StringMsg以及發送的消息message外還要指定發送的客戶端IDlastClient.connectionId。此爲消息發送的整個流程。也能夠經過networkReader和networkWriter進行消息的發送與讀取
//-------------------------------------------代碼--------------------------------------------//
主代碼:
using UnityEngine; using UnityEngine.Networking; using System; using UnityEngine.Networking.NetworkSystem; using System.Collections.Generic; using System.Text; public class MyNetworkManager : MonoBehaviour { public string ip = "127.0.0.1"; public int port = 7777; public GameObject localPlayer; public static MyNetworkManager Instance; public NetworkClient unetClient; bool serverStarted = false; bool clientCreated = false; AddPlayerMessage addPlayerMsg = new AddPlayerMessage();//每一個player加入遊戲時的當心容器 Dictionary<NetworkConnection, string> allClients = new Dictionary<NetworkConnection, string>(); NetworkConnection lastClient;//測試用的 #region PUBLIC ATTRIBUTES public int numPlayers { get { int numPlayers = 0; for (int i = 0; i < NetworkServer.connections.Count; i++) { var conn = NetworkServer.connections[i]; if (conn == null) continue; for (int ii = 0; ii < conn.playerControllers.Count; ii++) { if (conn.playerControllers[ii].IsValid) { numPlayers += 1; } } } return numPlayers; } } public int numClients { get { return NetworkServer.connections.Count; } } #endregion #region CALLBACKS public Action onStartServer; public Action onStartHost; public Action<NetworkClient> onStartClient; public Action onStopServer; public Action onStopClient; public Action<NetworkMessage> onServerConncetedAction;//服務端接收到client事件 public Action<NetworkMessage> onServerDisconnectedAction;//客戶端斷開鏈接時服務端事件 public Action<NetworkMessage> onClientConncetedAction;//客戶端鏈接到server事件 public Action<NetworkMessage> onClientDisonncetedAction;//客戶端鏈接到server事件 #endregion #region PUBLIC METHODS public void StartServer(bool isServer = true) { if(!serverStarted) { NetworkServer.Listen(ip, port); RegisterServerMessage(); //Debug.Log("Server started..."); LogInfo.theLogger.Log("Server started..."); if(!isServer) { unetClient = ClientScene.ConnectLocalServer(); RegisterClientMessage(unetClient); LogInfo.theLogger.Log("Local Client Created..."); } if(onStartServer!=null) { onStartServer(); } } } public void StartClient() { if (!clientCreated) { unetClient = new NetworkClient(); RegisterClientMessage(unetClient); unetClient.Connect(ip, port); //Debug.Log("Client connecting..."); LogInfo.theLogger.Log("Client connecting..."); if (onStartClient != null) { onStartClient(unetClient); } } } public void StartHost() { StartServer(false); if(onStartHost!=null) { onStartHost(); } } public void StopServer(bool isServer = true) { foreach(var conn in NetworkServer.connections) { if(conn!=null) NetworkServer.DestroyPlayersForConnection(conn); } NetworkServer.Shutdown(); serverStarted = false; if (onStopServer != null) { onStopServer(); } if (!isServer) StopClient(); } public void StopClient() { unetClient.Disconnect(); unetClient.Shutdown(); unetClient = null; ClientScene.DestroyAllClientObjects(); clientCreated = false; if(onStopClient!=null) { onStopClient(); } } #endregion #region PRIVATE METHODS void RegisterServerMessage() { NetworkServer.RegisterHandler(MsgType.Connect, OnServerConnect); NetworkServer.RegisterHandler(MsgType.Disconnect, OnServerDisconnect);//若是stopServer則不會觸發此事件,只有客戶端主動斷開鏈接(stopclient)時觸發 NetworkServer.RegisterHandler(MsgType.AddPlayer, OnServerAddPlayer); NetworkServer.RegisterHandler(MsgType.RemovePlayer, OnServerRemovePlayer); NetworkServer.RegisterHandler(CustomerMsgType.StringMsg, OnServerReceiveStringMsg); } void RegisterClientMessage(NetworkClient client) { client.RegisterHandler(MsgType.Connect, OnClientConnect); client.RegisterHandler(MsgType.Disconnect, OnClientDisconnect);//服務端主動斷開或者stopServer時觸發,但客戶端主動斷開鏈接(stopClient)時不會觸發 client.RegisterHandler(CustomerMsgType.StringMsg, OnClientReceiveStringMsg); if (localPlayer!=null) { ClientScene.RegisterPrefab(localPlayer); } } //server端接收到client鏈接觸發 void OnServerConnect(NetworkMessage msg) { NetworkConnection conn = msg.conn; lastClient = conn; if (onServerConncetedAction != null) { onServerConncetedAction(msg); } Debug.Log("Client " + conn.connectionId + " is connected..."); LogInfo.theLogger.Log("Client " + conn.connectionId + " is connected..."); } //client斷開鏈接時,server觸發 void OnServerDisconnect(NetworkMessage msg) { NetworkConnection conn = msg.conn; NetworkServer.DestroyPlayersForConnection(conn);//斷開全部與conn對應的遊戲物體 conn.Disconnect(); conn.Dispose(); if (onServerDisconnectedAction != null) { onServerDisconnectedAction(msg); } LogInfo.theLogger.Log("Client " + conn.connectionId + " is disconnected..."); } void OnServerAddPlayer(NetworkMessage msg) { //var message = msg.ReadMessage<CustomerMsgType.StringMessage>(); //Debug.Log(message.stringMsg); var player = Instantiate(localPlayer); msg.ReadMessage(addPlayerMsg); //Debug.Log(Encoding.Default.GetString(addPlayerMsg.msgData)); NetworkServer.AddPlayerForConnection(msg.conn, player, addPlayerMsg.playerControllerId); LogInfo.theLogger.Log("Client "+msg.conn.connectionId+" is added"); } void OnServerRemovePlayer(NetworkMessage msg) { LogInfo.theLogger.Log("Player removded"); } void OnServerReceiveStringMsg(NetworkMessage msg) { var receivedMsg = msg.ReadMessage<CustomerMsgType.StringMessage>(); //var newMsg = "Server received:" + receivedMsg.stringMsg; LogInfo.theLogger.Log(receivedMsg.stringMsg); var message = new CustomerMsgType.StringMessage(); message.stringMsg = "Server received:" + receivedMsg.stringMsg ; NetworkServer.SendToClient(lastClient.connectionId, CustomerMsgType.StringMsg, message); } //client鏈接到server時觸發 void OnClientConnect(NetworkMessage msg) { NetworkConnection conn = msg.conn; ClientScene.Ready(conn); CustomerMsgType.StringMessage stringMsg = new CustomerMsgType.StringMessage(); stringMsg.stringMsg = "ttt"; ClientScene.AddPlayer(conn, (short)conn.connectionId, stringMsg); if (onClientConncetedAction != null) { onClientConncetedAction(msg); } Debug.Log("Client is connected to server..."); LogInfo.theLogger.Log("Client is connected to server..."); } void OnClientDisconnect(NetworkMessage msg) { //NetworkConnection conn = msg.conn; StopClient(); if (onClientDisonncetedAction != null) { onClientDisonncetedAction(msg); } LogInfo.theLogger.Log("Client is disconnected to server..."); } void OnClientReceiveStringMsg(NetworkMessage msg) { var receivedMsg = msg.ReadMessage<CustomerMsgType.StringMessage>(); LogInfo.theLogger.Log(receivedMsg.stringMsg); } #endregion private void Start() { Application.runInBackground = true; Instance = this; } }
消息代碼:
using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; public class CustomerMsgType { public const short StringMsg = 101; public const short FileMsg = 102; public class StringMessage : MessageBase//此類unet有定義,此處定義畫蛇添足 { public string stringMsg; } public class FileMessage : MessageBase { public string fileType; public byte[] fileContent; } }