基于LiteNetLib的Server/Client Demo
先上效果:
再分享代码:
public class BaseDefine
{/// <summary>/// 通讯端口/// </summary>public static readonly int PORT = 9050;/// <summary>/// 连接秘钥/// </summary>public static readonly string NETWORK_KEY = "KEY_CPR_NET";/// <summary>/// 最大客户端连接数/// </summary>public static readonly int MaxConnectAttempts = 10;/// <summary>/// 客户端广播的间隔时间/// </summary>public static float ClientBroadcastTimeSpace = 1;/// <summary>/// 客户端广播的数据,以及服务器向正在广播的客户端发送的数据/// </summary>public static byte UnconnectedMessageValue = 1;
}
public enum EnumCprDataType
{/// <summary>/// 按压深度/// </summary>CprDatDeep,/// <summary>/// 吹气量/// </summary>CprDatAir,/// <summary>/// 开放气道/// </summary>CprDatOpenBreath
}/// <summary>
/// 数据类型
/// </summary>
[System.Serializable]
public class CPRDataType
{public EnumCprDataType datType;public int datValue;
}
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using LiteNetLib;
using LiteNetLib.Utils;
using UnityEngine;
using UnityEngine.Events;
using Random = UnityEngine.Random;public class NetworkServer : MonoBehaviour, INetEventListener
{private NetManager _server;private NetDataWriter _writer = new NetDataWriter();/// <summary>/// 存储已连接的所有NetPeer对象/// </summary>private List<NetPeer> _netPeers = new List<NetPeer>();/// <summary>/// NetPeer连接/断开事件/// </summary>public UnityEvent<bool,string> EnvPeerChanged = new UnityEvent<bool, string>();/// <summary>/// 其他网络相关消息更新事件/// </summary>public UnityEvent<string> EnvNetworkMessageUpdate = new UnityEvent<string>();void Start(){_server = new NetManager(this);// 启用广播接收_server.BroadcastReceiveEnabled = true;// 设置最大连接数_server.MaxConnectAttempts = BaseDefine.MaxConnectAttempts;// // 设置服务器更新频率// _server.UpdateTime = 15;_server.Start(BaseDefine.PORT);}void Update(){_server.PollEvents();if (Input.GetKeyDown(KeyCode.Alpha0)){SendSimulateData(EnumCprDataType.CprDatDeep);}else if (Input.GetKeyDown(KeyCode.Alpha1)){SendSimulateData(EnumCprDataType.CprDatAir);}else if (Input.GetKeyDown(KeyCode.Alpha2)){SendSimulateData(EnumCprDataType.CprDatOpenBreath);}}private void OnDestroy(){EnvPeerChanged.RemoveAllListeners();EnvNetworkMessageUpdate.RemoveAllListeners();EnvPeerChanged = null;EnvNetworkMessageUpdate = null;}void OnApplicationQuit(){if (_server != null){//断开所有客户端_server.DisconnectAll();//停止网络服务_server.Stop();}_server = null;//释放NetDataWriter_writer.Reset();_writer = null;}/// <summary>/// 模拟广播数据/// </summary>/// <param name="datType"></param>void SendSimulateData(EnumCprDataType datType){var data = new CPRDataType{datType = datType,datValue = (int)Random.Range(0f, 100f)};_writer.Reset();_writer.Put(JsonUtility.ToJson(data));//可靠有序传输(保证顺序)_server.SendToAll(_writer, DeliveryMethod.ReliableOrdered);EnvNetworkMessageUpdate?.Invoke($"SendData:{datType.ToString()},{data.datValue}");}#region OverWritepublic void OnPeerConnected(NetPeer peer){if (!_netPeers.Contains(peer)){Debug.Log("[SERVER] We have new peer " + peer.EndPoint);_netPeers.Add(peer);EnvPeerChanged?.Invoke(true, peer.EndPoint.ToString());EnvNetworkMessageUpdate?.Invoke($"[SERVER] We have new peer " + peer.EndPoint);}}public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo){if (_netPeers.Contains(peer)){Debug.Log("[SERVER] peer disconnected " + peer.EndPoint + ", info: " + disconnectInfo.Reason);_netPeers.Remove(peer);EnvPeerChanged?.Invoke(false, peer.EndPoint.ToString());EnvNetworkMessageUpdate?.Invoke($"[SERVER] peer disconnected " + peer.EndPoint + ", info: " + disconnectInfo.Reason);}}public void OnNetworkError(IPEndPoint endPoint, SocketError socketError){Debug.Log("[SERVER] error " + socketError);EnvNetworkMessageUpdate?.Invoke("[SERVER] error " + socketError);}public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod){// 临时字符串用于存储接收到的消息string tmpstring = "";// 尝试从读取器中获取字符串reader.TryGetString(out tmpstring);print("Server.OnNetworkReceive:" + tmpstring);EnvNetworkMessageUpdate?.Invoke("Server.OnNetworkReceive:" + tmpstring);}public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType){int intValue = reader.GetInt();// 如果消息类型是广播if (messageType == UnconnectedMessageType.Broadcast && intValue == BaseDefine.UnconnectedMessageValue){Debug.Log($"[SERVER] Received discovery request:{intValue}. Send discovery response.");// 创建响应数据写入器NetDataWriter resp = new NetDataWriter();// 放入一个整数resp.Put(BaseDefine.UnconnectedMessageValue);// 发送未连接的消息_server.SendUnconnectedMessage(resp, remoteEndPoint);resp = null;EnvNetworkMessageUpdate?.Invoke("[SERVER] Received discovery request. Send discovery response");}}public void OnNetworkLatencyUpdate(NetPeer peer, int latency){// throw new System.NotImplementedException();}public void OnConnectionRequest(ConnectionRequest request){if (_server.ConnectedPeersCount > _server.MaxConnectAttempts) return;// 如果请求的密钥正确,则接受请求request.AcceptIfKey(BaseDefine.NETWORK_KEY);}#endregion}
using System.Net;
using System.Net.Sockets;
using LiteNetLib;
using UnityEngine;
using UnityEngine.Events;public class NetworkClient : MonoBehaviour, INetEventListener
{private NetManager _client;private NetPeer _netPeer;private float _time = 0;/// <summary>/// NetPeer连接/断开事件/// </summary>public UnityEvent<bool,string> EnvPeerChanged = new UnityEvent<bool, string>();/// <summary>/// 其他网络相关消息更新事件/// </summary>public UnityEvent<string> EnvNetworkMessageUpdate = new UnityEvent<string>();void Start() {_client = new NetManager(this);_client.UnconnectedMessagesEnabled = true;_client.Start();// _client.Connect(BaseDefine.serverIP, BaseDefine.port, BaseDefine.NETWORK_KEY);}void Update(){_client.PollEvents();//如果没有连接到服务器,固定间隔时间广播数据出去var peer = _client.FirstPeer;if (peer == null || peer.ConnectionState != ConnectionState.Connected){_time++;if (_time >= BaseDefine.ClientBroadcastTimeSpace){_time = 0;//这里可以广播任意消息_client.SendBroadcast(new byte[] { BaseDefine.UnconnectedMessageValue }, BaseDefine.PORT);}}}private void OnDestroy(){EnvPeerChanged.RemoveAllListeners();EnvNetworkMessageUpdate.RemoveAllListeners();EnvPeerChanged = null;EnvNetworkMessageUpdate = null;}void OnApplicationQuit(){if(_client != null){// 发送断开请求_client.DisconnectAll();// 停止网络服务_client.Stop();// 确保垃圾回收System.GC.Collect();}_client = null;_netPeer = null;}#region OverWritepublic void OnPeerConnected(NetPeer peer){Debug.Log("[CLIENT] We connected to " + peer.EndPoint);_netPeer = peer;EnvPeerChanged?.Invoke(true, peer.EndPoint.ToString());EnvNetworkMessageUpdate?.Invoke($"[CLIENT] We connected to " + peer.EndPoint);}public void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo){Debug.Log("[CLIENT] We disconnected because " + disconnectInfo.Reason);_netPeer = null;EnvPeerChanged?.Invoke(false, peer.EndPoint.ToString());EnvNetworkMessageUpdate?.Invoke($"[CLIENT] We disconnected because " + disconnectInfo.Reason);}public void OnNetworkError(IPEndPoint endPoint, SocketError socketError){Debug.Log("[CLIENT] We received error " + socketError);EnvNetworkMessageUpdate?.Invoke($"[CLIENT] We received error " + socketError);}public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber,DeliveryMethod deliveryMethod){var data = JsonUtility.FromJson<CPRDataType>(reader.GetString());Debug.Log($"Received {data.datType} data: {data.datValue}");EnvNetworkMessageUpdate?.Invoke($"[CLIENT] Received {data.datType} data: {data.datValue}");}public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader,UnconnectedMessageType messageType){if (messageType == UnconnectedMessageType.BasicMessage&& _client.ConnectedPeersCount == 0&& reader.GetInt() == BaseDefine.UnconnectedMessageValue){Debug.Log("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint);_client.Connect(remoteEndPoint, BaseDefine.NETWORK_KEY);EnvNetworkMessageUpdate?.Invoke("[CLIENT] Received discovery response. Connecting to: " + remoteEndPoint);}}public void OnNetworkLatencyUpdate(NetPeer peer, int latency){// throw new System.NotImplementedException();}public void OnConnectionRequest(ConnectionRequest request){throw new System.NotImplementedException();}#endregion
}
using System.Collections;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.UI;public class UpdateUIOfNetworkMessage : MonoBehaviour
{public enum EnumNetworkType{Server,Client}[SerializeField] private EnumNetworkType currNetworkType = EnumNetworkType.Server;[SerializeField] [ShowIf("currNetworkType", EnumNetworkType.Server)]public NetworkServer Server;[SerializeField] [ShowIf("currNetworkType", EnumNetworkType.Client)]public NetworkClient Client;/// <summary>/// 连接对象Item/// </summary>[SerializeField] private GameObject contentPeerItem;[SerializeField] private Scrollbar _scrollbarPeer;private Dictionary<string, GameObject> dicPeers = new Dictionary<string, GameObject>();/// <summary>/// 消息Item/// </summary>[SerializeField] private GameObject contentMessageItem;[SerializeField] private Scrollbar _scrollbarMessage;private void Start(){if (Server != null){Server.EnvPeerChanged.AddListener(OnEnvPeerChanged);Server.EnvNetworkMessageUpdate.AddListener(OnEnvNetworkMessageUpdate);}if (Client != null){Client.EnvPeerChanged.AddListener(OnEnvPeerChanged);Client.EnvNetworkMessageUpdate.AddListener(OnEnvNetworkMessageUpdate);}}/// <summary>/// 连接对象更新/// </summary>/// <param name="isConnect"></param>/// <param name="endPoint"></param>private void OnEnvPeerChanged(bool isConnect, string endPoint){if (!dicPeers.ContainsKey(endPoint)){if (isConnect){contentPeerItem.SetActive(true);var newPeer = GameObject.Instantiate(contentPeerItem, contentPeerItem.transform.parent);newPeer.GetComponentInChildren<Text>().text = endPoint;contentPeerItem.SetActive(false);dicPeers.Add(endPoint, newPeer);_scrollbarPeer.value = 0;Canvas.ForceUpdateCanvases();}}else{if (!isConnect){GameObject peerObj = null;bool isTrue = dicPeers.TryGetValue(endPoint, out peerObj);if (isTrue){dicPeers.Remove(endPoint);DestroyImmediate(peerObj);_scrollbarPeer.value = 0;Canvas.ForceUpdateCanvases();}}}}/// <summary>/// 消息更新/// </summary>/// <param name="message"></param>private void OnEnvNetworkMessageUpdate(string message){contentMessageItem.SetActive(true);var newMessage = GameObject.Instantiate(contentMessageItem, contentMessageItem.transform.parent);newMessage.GetComponentInChildren<Text>().text = message;contentMessageItem.SetActive(false);_scrollbarMessage.value = 0;Canvas.ForceUpdateCanvases();}
}
using System.Threading;
using UnityEngine;
/// <summary>
/// 实现程序单进程
/// </summary>
public class SingletonApp : MonoBehaviour
{private static Mutex _mutex;private const string APP_GUID = "UNITY_APP_GUID_SingletonApp";void Awake(){bool createdNew;_mutex = new Mutex(true, APP_GUID, out createdNew);if (!createdNew){Debug.LogError("程序已在运行中");Application.Quit();return;}DontDestroyOnLoad(gameObject);}void OnApplicationQuit(){if (_mutex != null){_mutex.ReleaseMutex();_mutex.Dispose();}}
}
参考链接:
LiteNetLib:轻量级可靠UDP网络库详解