先上效果:
再分享代码:
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 OverWrite
public 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 OverWrite
public 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网络库详解