文章目录
前言
根据对NetWorkServer 以及NetworkClient的理解,编写一个简易版的NetworkManager。NetWorkServer 以及NetworkClient是经典的服务端客户端的高级封装,更方便使用,本例为破产版NetworkManager,所有服务端客户端揉合在一起,可以自己选择是客户端还是服务端。unity提供的网络变成api中,有highLevel api,即高度封装的,也有low Level api,即最接近socket的api。惯例全部代码放在最后.
1.主要内容
1.1 NetWorkServer与NetworkClient机制
网络中一个Server对应对各client,unet NetWorkServer主要为静态方法,通过绑定ip地址以及端口就可以监听连接,client通过connect进行连接,当连接成功后通过注册的相关消息进行连接成功与否的方法进行相应处理(采用socket编程时一般通过回调进行处理,在unet中稍有不同,但原理一样)
1.1.1 server进行监听,并注册监听事件
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端要注册相对应的消息类型。
1.1.2 client注册事件,并连接
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);
}
}
1.2 clientScene场景管理
通过上一步,实现了Server与client的连接,一个client与server连接成功后对应一个networkconnection,通过连接绑定相应的游戏物体来进行操作则需要clientScene。clientScene组要为静态方法,统一管理连接的游戏物体。
1.2.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);
1.2.2 添加player
连接成功后通过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.3 流程梳理
本部分为对整个流程的梳理,步骤如下:
1)服务端开启监听
2)客户端连接
3)连接后客户端向clientscene添加玩家
4)服务端接收到玩家添加成功的消息,并创建玩家
1.4 消息发送
客户端服务端发送消息时,要先注册消息类型以及对应的方法,通过NetworkClient的send方法以及NetworkServer的send方进行发送。
1.4.1 注册消息
如步骤(一)中的client.RegisterHandler(CustomerMsgType.StringMsg, OnClientReceiveStringMsg)(客户端) NetworkServer.RegisterHandler(CustomerMsgType.StringMsg, OnServerReceiveStringMsg)(服务端)
1.4.2 消息发送
向服务端发送消息,发送时包含发送的消息主体stringMsg(为继承MessageBase的类型)以及发送消息的类型CustomerMsgType.StringMsg。
public void Send(string msg)
{
CustomerMsgType.StringMessage stringMsg = new global::CustomerMsgType.StringMessage();
stringMsg.stringMsg = msg;
unetClient.Send(CustomerMsgType.StringMsg, stringMsg);
}
1.4.3 接收读取
所有注册的消息对应的方法都含有一个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进行消息的发送与读取
2.代码
2.1 NetworkManager破产版
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;
}
}
2.2 消息代码
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;
}
}