效果展現(還沒有完善)
ide
User Data Protocol 用戶數據報協議工具
UDP是不鏈接的數據報模式。即傳輸數據以前源端和終端不創建鏈接。使用盡最大努力交付原則,即不保證可靠交付。ui
數據報模式:因爲不創建鏈接,收到的數據多是任意主機發送的,因此接收端Read次數必須與發送端Write次數相同,每次只接收一個報文,避免多個報文合併。但若是報文過長,多出部分會被丟棄,因此注意數據最大爲1472字節。this
服務端 客戶端spa
獲取本機終結點 獲取本機終結點線程
建立UdpClient對象 建立UdpClient對象 code
接收任意終結點消息 接收任意終結點消息orm
向客戶端發送消息 向服務端發送消息server
…… … 對象
關閉鏈接 關閉鏈接
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; using System.Net; using System.Net.Sockets; using System.Text; using Common; using UIWidgetsSamples; using System; /// <summary> /// 服務端 /// </summary> public class ChatUDPServerTest : MonoBehaviour { public string serverIP; //IP地址 public int serverPort; //端口 //1.建立Scoket對象 IP Port private Thread thread; private UdpClient udpSeivic; public void Start() { chatView = transform.FindChildByName("ChatView"). GetComponent<ChatView>(); //給端口和IP //構建終結點 IP和一個端口 IPEndPoint localEP = new IPEndPoint(IPAddress.Parse(serverIP), serverPort); udpSeivic = new UdpClient(localEP); thread = new Thread(ReceiveMessage); thread.Start(); } /// <summary> /// 接收消息 /// </summary> private void ReceiveMessage() { while (true) { IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0); //建立任意終結點 //ref byte[] date = udpSeivic.Receive(ref remote); //Receive接收消息 若是沒有收到消息 線程阻塞 放在線程中 string msg = Encoding.UTF8.GetString(date); //獲取的客戶都安信息 Debug.Log(remote.Address + "===" + remote.Port); //若是接收客戶端的消息,會把任意終結點修改成客戶端的終結點 ThreadCrossHelper.Instance.ExecuteOnMainThread(() => { ShowMessage(msg); }); } } private ChatView chatView; /// <summary> /// 顯示消息 /// </summary> /// <param name="msg"></param> public void ShowMessage(string msg) { chatView.DataSource.Add(new ChatLine() { UserName = "AnnnS", Message = msg, Time = DateTime.Now, Type = ChatLineType.User, }); } private void OnApplicationQuit() { udpSeivic.Close(); thread.Abort(); } }
腳本引用的工具箱
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Common { /// <summary> /// /// </summary> public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> { //public static T Instance //{ // get; // private set; //} //private void Awake() //{ // Instance = this as T; //} //按需加載 private static T instance; public static T Instance { get { if (instance == null) { //在場景中查找對象 instance = FindObjectOfType<T>(); if (instance == null) { //建立遊戲對象 附加 腳本對象 new GameObject("Singleton of " + typeof(T)).AddComponent<T>();//當即執行Awake } else { instance.Initialized(); } } return instance; } } protected virtual void Initialized() { } [Tooltip("是否須要跨場景不銷燬")] public bool isDontDestroy = true; //若是管理類自行附加到物體中 //在Awake中爲instance賦值 protected void Awake() { if (isDontDestroy) { DontDestroyOnLoad(gameObject); } if (instance == null) { instance = this as T; instance.Initialized(); } } } }
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Common { /// <summary> /// /// </summary> public class ThreadCrossHelper : MonoSingleton<ThreadCrossHelper> { /// <summary> /// 延遲項 /// </summary> class DelayedItem { public Action CurrentAction { get; set; } public DateTime Time { get; set; } } private List<DelayedItem> actionList; //private List<Action> actionList; //private List<float> timeList; protected override void Initialized() { base.Initialized(); actionList = new List<DelayedItem>(); } private void Update() { for (int i = actionList.Count - 1; i >= 0; i--) { //到時間 if (actionList[i].Time <= DateTime.Now) { lock (actionList) { actionList[i].CurrentAction();//執行 actionList.RemoveAt(i);//從列表中移除 } } } } /// <summary> /// 爲子線程提供,能夠在主線程中執行的方法 /// </summary> /// <param name="action"></param> /// <param name="dealy"></param> public void ExecuteOnMainThread(Action action, float dealy = 0) { DelayedItem item = new DelayedItem() { CurrentAction = action, //Time = Time.time + dealy Time = DateTime.Now.AddSeconds(dealy) }; lock (actionList) { actionList.Add(item); } } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Common { /// <summary> /// 變換組件助手類 /// </summary> public static class TransformHelper { /// <summary> /// 未知層級,根據名稱查找後代元素 /// </summary> /// <param name="currentTF"></param> /// <param name="childName"></param> /// <returns></returns> public static Transform FindChildByName(this Transform currentTF, string childName) { Transform childTF = currentTF.Find(childName); if (childTF != null) return childTF; //將問題推遲給子物體 for (int i = 0; i < currentTF.childCount; i++) { //在方法體內部,又遇到了相同的問題,因此須要調用自身。 childTF = FindChildByName(currentTF.GetChild(i), childName); if (childTF != null) return childTF; } return null; } } }