效果展示 網路編程 --使用UDP協議的簡單聊天室 由於TCP協議是一種面向連接的協議,有三次揮手通信確認機制,且數據包沒有代銷限制,TCP協議確保在通信雙方都收到對方確認信息的前提下,才開始為通信的雙方傳輸信息(數據包)! 這樣數據包的傳輸可以得到安全保障!所以簡單的理解為安全的!當然一旦,出現網 ...
效果展示
網路編程 --使用UDP協議的簡單聊天室
由於TCP協議是一種面向連接的協議,有三次揮手通信確認機制,且數據包沒有代銷限制,TCP協議確保在通信雙方都收到對方確認信息的前提下,才開始為通信的雙方傳輸信息(數據包)!
這樣數據包的傳輸可以得到安全保障!所以簡單的理解為安全的!當然一旦,出現網路問題,或者一些其他意外現象造成通信雙方的連接出現異常或斷開連接,使用TCP是不能傳輸信息的!
UDP不是一種面向連接的協議,就是說,在使用UDP協議進行數據交互的時候,彼此可以不連接,通信的一端突然地向另外一端發送數據包,但是對方沒有啟動接收程式,那麼對方當時是收不到數據包的,或者當你發送數據的過程中,突然出現意外斷網,停電等等意外現象,都有可能造成對方收不到信息!也就說,你發送的信息對方沒收到,同時你也不知道對方有沒有收到!這不好!
載入場景
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Net.Sockets; using System.Text; using Common; using UnityEngine.UI; using System; using System.Net; using UIWidgetsSamples; using UnityEngine.SceneManagement; namespace NS { ///<summary> /// 程式入口 ///</summary> public class GameMain : MonoSingleton<GameMain> { private InputField IPInputField; private InputField IPInputField1; //點擊按鈕 ButtonClientEnter初始化客戶端服務類 private void Start() { transform.FindChildByName("ButtonClientEnter"). GetComponent<Button>().onClick.AddListener(OnEnterChatScene); //獲取進入按鈕 transform.FindChildByName("ButtonServerEnter"). GetComponent<Button>().onClick.AddListener(OnEnterServerScene); // InputServerIPForClient IPInputField1 = transform.FindChildByName("" + "InputServerIPForClient").GetComponent<InputField>(); IPInputField = transform.FindChildByName("" + "InputServerIPForServer").GetComponent<InputField>(); //獲取文本框 拿到IP } private void OnEnterServerScene() { UDPServerNetWorkService.Instance.Initialized(IPInputField.text); SceneManager.LoadScene("Server"); } //進去聊天室 private void OnEnterChatScene() { UDPClientNetWorkService.Instance.Initialized (IPInputField1.text); //獲取IP 初始化客戶都安 SceneManager.LoadScene("Chat"); //切換到聊天視窗 載入不刪除 } //載入場景 } }GameMain
Date數據類
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using System.Net.Sockets; using System.Text; using Common; using UnityEngine.UI; using System; using System.Net; using UIWidgetsSamples; using System.Runtime.Serialization.Formatters.Binary; namespace NS { ///<summary> ///聊天消息 ///</summary> [System.Serializable]//表示類可以被序列化 public class ChatMessage { //消息類型 0 1 2 枚舉 public UDPNetWork Type { get; set; } //發送者名稱 public string SenderName { get; set; } //消息內容 public string Content { get; set; } //如何把一個對象轉位元組數組??? //序列化:將對象狀態存儲到某種介質的過程(場景還原,網路交互) //存儲介質:磁碟 記憶體 網路 //反序列化:將某種介質中的對象轉換成C#對象 public byte [] ObjectToBytes() { //1.二進位寫入器 往記憶體流去寫 支持跨平臺 //MemoryStream ms = new MemoryStream(); //BinaryWriter writer = new BinaryWriter(ms); //byte[] bys = Encoding.UTF8.GetBytes(SenderName); ////值類型 因為大小固定 所以直接Write ////引用類型 大小不固定 所以需要寫入長度 //writer.Write(bys.Length); ////寫入長度 //writer.Write(bys); //寫入內容 //2.序列化 //使用Using 可以在退出代碼塊的時候 自動關閉非托管資源 using (MemoryStream ms = new MemoryStream()) { //記憶體流 BinaryFormatter fomatter = new BinaryFormatter(); //二進位格式化器 fomatter.Serialize(ms, this); //序列化 將當前對象序列化到記憶體流中 //byte[] result = ms.ToArray(); //從記憶體流中獲取位元組數組 return ms.ToArray(); } } public static ChatMessage BytesToObject(byte [] bytes) { using (MemoryStream ms = new MemoryStream(bytes)) { //記憶體流 BinaryFormatter fomatter = new BinaryFormatter(); //二進位格式化器 return fomatter.Deserialize(ms) as ChatMessage; //反序列化 將記憶體流中 中的位元組反序列化 } //自動釋放資源 } } }ChatMessage
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.Net; namespace NS { ///<summary> /// 事件參數類 ///</summary> public class MessageArrivedEventArgs { public ChatMessage Message { get; set; } public DateTime DateTime { get; set; } public IPEndPoint senderEP { get; set; } } }MessageArrivedEventArgs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace NS { ///<summary> /// 消息類型 ///</summary> public enum UDPNetWork { Online, //上線 Offline, //下線 General //普通 } }UDPNetWork
NetWork Service(網路服務)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using Common; using System.Net.Sockets; using System.Text; using UnityEngine.UI; using System.Net; using UIWidgetsSamples; using System.Threading; namespace NS { ///<summary> ///基於UDP協議 客戶端網路服務類 ///</summary> public class UDPClientNetWorkService : MonoSingleton<UDPClientNetWorkService> { //事件參數類 傳遞信息 public event Action<MessageArrivedEventArgs> MessageArrivedEventHandler; //初始化socket對象 ,由開始界面的Game Main類調用 private Thread receiverThread; //線程 private UdpClient udpServer; public void Initialized(string serverIP) { //載入不刪除對象 udpServer = new UdpClient(); //客戶端發送消息再給終結點 serverEP = new IPEndPoint(IPAddress.Parse(serverIP), 8989); Debug.Log(serverEP.Address); //分配IP和埠 沒有保存 udpServer.Connect(serverEP); //Connect方法建立預設遠程主機使用中指定的值endPoint參數。 //一旦建立,無需在每次調用中指定遠程主機Send方法。 receiverThread = new Thread(ReceiveMessage); receiverThread.Start(); //提示上線 SendChatMessage(new ChatMessage() { Type = UDPNetWork.Online }); } private IPEndPoint serverEP; //發送消息 send按鈕 public void SendChatMessage(ChatMessage msg) { byte[] dgram = msg.ObjectToBytes(); //獲取報文 udpServer.Send(dgram, dgram.Length); //發送需要埠和IP Debug.Log(msg.Type); } //接收消息 接收阻塞 線程 public void ReceiveMessage() { while (true)//不停接收數據 { //接收 IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0); byte[] date = udpServer.Receive(ref remote); ChatMessage msg = ChatMessage.BytesToObject(date); Debug.Log(msg.Type); //引發事件 if (MessageArrivedEventHandler != null) { //線程交叉助手類調用 因為訪問UnityAPI ThreadCrossHelper.Instance.ExecuteOnMainThread(() => { var args = new MessageArrivedEventArgs() { Message = msg, DateTime = DateTime.Now }; //創建事件參數類 參數沒有賦值??? MessageArrivedEventHandler(args); //調用方法不需要傳參數嗎??? }); } } } //關閉 public void OnApplicationQuit() { SendChatMessage(new ChatMessage() { Type = UDPNetWork.Offline }); receiverThread.Abort(); udpServer.Close(); } } }UDPClientNetWorkService
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; using Common; using System.Net.Sockets; using System.Text; using UnityEngine.UI; using System.Net; using UIWidgetsSamples; using System.Threading; using System; namespace NS { ///<summary> /// 基於UDP的服務端網路服務類 ///</summary> public class UDPServerNetWorkService : MonoSingleton<UDPServerNetWorkService> { //客戶端列表 //初始化Socket //發送消息 //接收消息 //事件參數類 傳遞信息 public event Action<MessageArrivedEventArgs> MessageArrivedEventHandler; //初始化socket對象 ,由開始界面的Game Main類調用 private Thread receiverThread; //線程 private UdpClient udpServer; public void Initialized(string serverIP) { //載入不刪除對象 //客戶端發送消息再給終結點 serverEP = new IPEndPoint(IPAddress.Parse(serverIP), 8989); udpServer = new UdpClient(serverEP); receiverThread = new Thread(ReceiveMessage); receiverThread.Start(); //註冊事件 收到消息的時候執行 MessageArrivedEventHandler += OnMessageArrived; } //收到消息的時候分發 private void OnMessageArrived(MessageArrivedEventArgs obj) { switch (obj.Message.Type) { case UDPNetWork.Online: //存列表 AddClient(obj.senderEP); break; case UDPNetWork.Offline: //移除列表 RemoveClient(obj.senderEP); break; case UDPNetWork.General: //轉發 TransmitMessage(obj.Message); break; } } private IPEndPoint serverEP; //發送消息 send按鈕 public void SendChatMessage(ChatMessage msg,IPEndPoint remote) { byte[] dgram = msg.ObjectToBytes(); //獲取報文 udpServer.Send(dgram, dgram.Length, remote); //發送需要埠和IP } //接收消息 接收阻塞 線程 public void ReceiveMessage() { while (true) //不停接收數據 { //接收 IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0); byte[] date = udpServer.Receive(ref remote); ChatMessage msg = ChatMessage.BytesToObject(date); Debug.Log(msg.Type + "adsadad"); //引發事件 if (MessageArrivedEventHandler != null) { //線程交叉助手類調用 因為訪問UnityAPI ThreadCrossHelper.Instance.ExecuteOnMainThread(() => { var args = new MessageArrivedEventArgs() { Message = msg, DateTime = DateTime.Now, senderEP = remote }; Debug.Log(msg + "1"); //創建事件參數類 參數沒有賦值??? MessageArrivedEventHandler(args); //調用方法不需要傳參數嗎??? Debug.Log(msg + "2"); }); } } } //關閉 public void OnApplicationQuit() { receiverThread.Abort(); udpServer.Close(); } private List<IPEndPoint> allListEP = new List<IPEndPoint>(); //添加客戶端 public void AddClient(IPEndPoint clientEP) { allListEP.Add(clientEP); } //移除客戶端 public void RemoveClient(IPEndPoint clientEP) { allListEP.Remove(clientEP); } //轉發消息 private void TransmitMessage(ChatMessage msg) { foreach (var item in allListEP) { SendChatMessage(msg, item); //根據不同埠發送消息 } } } }UDPServerNetWorkService
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Net.Sockets; using System.Text; using Common; using UnityEngine.UI; using System; using System.Net; using UIWidgetsSamples; namespace NS { ///<summary> ///客戶端顯示信息界面類 ///</summary> public class ChatClient : MonoBehaviour { private InputField messageInput; private InputField nameInput; private void Start() { messageInput = transform.FindChildByName("MessageInput").GetComponent<InputField>(); nameInput = transform.FindChildByName("NameInput").GetComponent<InputField>(); transform.FindChildByName("Send").GetComponent<Button>(). onClick.AddListener(OnSendButtonClick); //獲取各種組建 chatview = transform.FindChildByName("ChatView").GetComponent<ChatView>(); UDPClientNetWorkService.Instance.MessageArrivedEventHandler += OnMessageArrived; //註冊事件 } //點擊調用發送消息的方法 private void OnSendButtonClick() { ChatMessage msg = new ChatMessage() { Type = UDPNetWork.General, Content = messageInput.text, SenderName = nameInput.text }; UDPClientNetWorkService.Instance. SendChatMessage(msg); } private ChatView chatview; private void OnMessageArrived(MessageArrivedEventArgs args) { //註冊方法 //顯示消息 ChatLine line = new ChatLine() { //事件參數對象賦值???? Time = args.DateTime, Message = args.Message.Content, Type = ChatLineType.User, UserName=args.Message.SenderName }; chatview.DataSource.Add(line); //滾動 chatview.ScrollRect.verticalNormalizedPosition = 0; } } }ChatClient
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Common; namespace NS { ///<summary> ///服務端顯示面板 ///</summary> public class ChatServer : MonoBehaviour { private void Start() { text = transform.FindChildByName("Text").GetComponent<Text>(); UDPServerNetWorkService.Instance.MessageArrivedEventHandler += OnMessageArrived; } private Text text; //當服務端收到消息 顯示在文本中 private void OnMessageArrived(MessageArrivedEventArgs obj) { Debug.Log(obj.Message.Type); text.text = string.Format("{0}---{1}", obj.Message.Type, obj.Message.Content); } } }ChatServer
工程包鏈接: https://github.com/AnsssAS/Assect