基于LiteNetLib的Server/Client Demo

发布于:2025-07-19 ⋅ 阅读:(21) ⋅ 点赞:(0)

先上效果:
在这里插入图片描述

再分享代码:

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网络库详解


网站公告

今日签到

点亮在社区的每一天
去签到