untiy Socket通信一篇通
文章目錄
- 一 前言
- 二 Socket簡介
- 什么是socket
- 什么是TCP/UDP
- 三 在untiy中使用Socket需要注意的問題
- 兩種不同的socket類
- 線程阻塞問題
- 接收到的消息不要在接收線程里直接處理
- 四 前置概念
- 命名空間
- IPAddress類
- IPEndPoint
- socket的同步和異步方法
- 五 TCP的使用
- 1 使用TCPClient
- 1.1同步方法
- 1.2異步方法
一 前言
網上有很多socket的使用教程,但是由于Untiy中使用socket有其特殊性,故整理本文,希望進行盡可能從實用角度介紹socket在untiy中的使用,并但是限于作者經驗不足,文中有錯誤的地方希望大佬可以積極斧正
有任何疑問歡迎留言
二 Socket簡介
什么是socket
Socket即TCP協議和UDP協議,可以通過網絡(IP地址和端口)實現不同設備之間的通信。TCP和UDP是socket的兩種不同實現,我們可以根據需要選擇一個或組合使用
什么是TCP/UDP
TCP 在通信前需要建立連接,為此需要區分監聽者和連接者,并且可以確保消息按順序可靠的到達,除非網絡不通或網絡太差
UDP不需要建立連接,只需要知道對方UDP的IP端口即可,理論上發送速度更快,但是如果發送中出現了消息丟失,那么消息就丟了(當然我們可以通過回執來手動重發)
TCP只能連TCP,UDP只能連UDP
三 在untiy中使用Socket需要注意的問題
兩種不同的socket類
untiy中實際上有兩種不同的Socket對象可以使用,一種類名是XXXClient,分為TcpClient和UdpClient,一種類名是就是Socket,通過不同的參數來區別是Tcp還是UDP,這兩種對象功能是相同的,API也是類似的,前者實際上是對后者的封裝
這兩種類使用一種即可
線程阻塞問題
socket在連接、發送、接收消息、監聽連接時都會阻塞線程。
對于發送,只要不是一個巨大的文件,正常的消息對程序的阻塞時間很短,我們通常直接同步發送即可。
對于連接,接收消息和TCP的監聽連接,由于沒有收到消息前,程序將卡住直到收到消息,所以我們必須開一個額外的線程用于處理接收,開線程的方式有兩種,一是手動創建一個新線程,而是使用socket的異步接收方法,自動創建新線程。這兩種方法下面都有演示,推薦使用異步方法,因為代碼更簡潔
接收到的消息不要在接收線程里直接處理
untiy是單線程的,且所有的對游戲對象的修改只能在主線程里進行,如果我們在接收線程里試圖修改,就會拋出異常。解決方法是可以將收到的消息放到一個列表里,然后由主線程里遍歷列表來處理消息
四 前置概念
命名空間
using System.Net; using System.Net.Sockets;IPAddress類
即IP,通常我們只使用他的靜態方法和靜態屬性,常用如下
IPAddress.Any; //指本機所有可用的IP,對于單網卡電腦,就是本機地址 IPAddress.Broadcast;//廣播地址,用于UDP廣播 IPAddress.Parse("255.255.255.255");//將一個字符串轉為IPAddress, 255.255.255.255是廣播地址,注意不要用127.0.0.1表示本機地址IPEndPoint
由一個IPAddress和一個Port組成,即一個IP地址,一個端口,這是確認一個通信地址的最小單位。用于綁定本機地址和指定要連接的遠端地址
//聲明一個新的IPEndPoint,參數1 IP地址 IPAddress.Any代表本機IP 參數2 端口 0代表自動選擇一個可用的端口 //這種聲明方式通常用于綁定本機地址 IPEndPoint localIPEndPoint = new IPEndPoint(IPAddress.Any,0);socket的同步和異步方法
對于connect,accept,send,receive這些方法,都有同步和異步兩個版本,其功能是相同的,同步版本如果在主線程里調會卡住線程,而異步版本會自動創建一個線程。此處以tcp的accept方法為例來解釋,其余的方法使用方式完全相同
同步版本:
TcpClient client = tcpListener.AcceptTcpClient();//沒有收到連接請求前,會一直卡住線程等待異步版本:就是將同步版本拆成了Begin和End兩個方法,再加一個回調函數
//參數1 回調函數 參數2 一個object,也就是可以是任何對象,通常我們傳入 socket對象本身,這里我們傳入監聽對象,因為回調里要用 tcpListener.BeginAcceptTcpClient(AcceptCallBack, tcpListener);//開始異步監聽 //接收到連接請求的回調函數private void AcceptCallBack(IAsyncResult ar)//參數的命名空間是 System,這里面存了Begin方法里傳入的參數2 的object {try{TcpListener listener = ar.AsyncState as TcpListener;//還原回當初傳入的參數tcpClient = listener.EndAcceptTcpClient(ar);//異步結束連接Debug.Log("已連接");//開啟下一個監聽連接tcpListener.BeginAcceptTcpClient(AcceptCallBack, tcpListener);}catch(Exception e){Debug.LogError("連接錯誤"+e.ToString());} }//需要說明的是,Begine方法里的參數2 我們可以傳入任何自定的類,不一定只傳socket對象,有時我們需要在回調里使用更多信息,就可以自定義一個類型,將需要的數據都放進去,然后傳入參數2,再在回調里還原為自定義的對象。但是必須要傳入結束異步線程用的對象看上去異步方法好像復雜了很多,但實際上為了解決同步方法帶來的阻塞問題,我們要付出的代價遠比使用異步方法大,因此建議只使用異步方法,可以在下文中“同步方法“ 和“異步方法”中自行體會
五 TCP的使用
1 使用TCPClient
分為手動創建線程和使用異步方法,可以混著用,推薦使用異步方法
1.1同步方法
不論是服務器還是客戶端,需要先建立連接才能發消息
以下為建立連接的方法:
監聽端:監聽端使用的類是TcpListener,注意不是TcpClient
連接端:
private TcpClient tcpClient = new TcpClient();//連上后,直接用這個對象和連接端通信private void Start(){//參數為服務器的地址,和服務器的TcpListener綁定的端口,注意tcpClient本身不需要指定IP端口,會自動分配tcpClient.Connect("192.168.1.36", 10000);//注意這也是一個阻塞方法,如果連不上服務器會導致程序卡住,自己開個連接線程}以下為通信的方法:
發送消息:
接收消息:
private TcpClient tcpClient = new TcpClient();private tcpReceiveT;private void Start(){//連接到服務器tcpClient.Connect("192.168.1.36", 10086);//接收消息的方法必須在子線程中執行tcpReceiveT= new Thread(RecciveMsg);tcpReceiveT.IsBackground = true;tcpReceiveT.Start();}/// <summary>/// 接收字節數組/// </summary>private void RecciveMsg(){//聲明接收緩沖區的長度byte[] receiveBuff = new byte[1024];int reviceLength = 0;while (true){try{//返回值為接收到的字節的數量 這一步會阻塞線程,決不能在主線程中執行reviceLength = tcpClient.Client.Receive(receiveBuff);//必須指定轉換的字節數組的長度,因為緩沖區剩余的空位會用0填充,這些0我們并不需要msg = Encoding.UTF8.GetString(receiveBuff,0,reviceLength);isReveive = true;print("收到的消息 "+msg);}}}1.2異步方法
顯然手動創建線程的代碼又臭又長,好在微軟爸爸為我們提供了方便的異步方法
以下為連接的方法:
監聽端:
連接端:
TcpClient tcpClient;private void Start(){tcpClient = new TcpClient();//開始異步連接 參數1 監聽端的ip 參數2 監聽端的 port 參數3 回調 參數4 回調函數里用的對象,這里用tcpClient本身tcpClient.BeginConnect(IPAddress.Parse("192.168.1.114"),1000, ConnectCallBack, tcpClient);}private void ConnectCallBack(IAsyncResult ar){try{TcpClient client = ar.AsyncState as TcpClient;client.EndConnect(ar);}catch (Exception e){Debug.LogError("連接錯誤" + e.ToString());}}以下為通信的方法:
發送還用同步發送即可
異步接收消息
UDP
UDP相對簡單,UDP并不需要連接,核心對象為UdpClinent和IPEndPoint
UdpClient即UDP的對象,用于收發消息
IPEndPoint可以理解為Udp通信的端點,解決三個問題 我是誰?我要發到哪里去?我從哪里接收?
聲明UdpClient時,需要指定UdpClient的IPEndPoint,即,我是誰
通過UdpClient發送消息時,需要指定目的地的IPEndPoint,即,我要發到哪里去
通過UdpClient接收消息時,需要指定監聽的IPEndPoint,即,我從哪里收
IPEndPoint常用的構造有以下兩種,都需要我們指定IP和端口,在構造函數中使用IPAddress類指定IP
//IP為當前可用IP,或任意IP 端口為0表示任何端口, 這種聲明通常在接收消息中使用 new IPEndPoint(IPAddress.Any, 0) //指定一個IP,255.255.255.255為群發 端口為40000 指定確定的IP端口通常用于聲明UdpClient和發送消息 new IPEndPoint(IPAddress.Parse("192.168.1.36"), 40000)發送
//公共的消息發送中心private UdpClient udpClient;private void Awake(){ //為UdpClient綁定本機的IP和端口 我是誰udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 40000));}/// <summary>/// 消息發送 IPEndPoint聲明 IPEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.36"),10000)/// </summary>public void CommonendMsgSender(string msg, IPEndPoint commonIPEndPoint){byte[] commonBuff = Encoding.UTF8.GetBytes(msg);udpClient.Send(commonBuff, commonBuff.Length, commonIPEndPoint);}監聽
private UdpClient CommonUdpClient;// 公共的消息接收端口private IPEndPoint commonReceiveIPEndPoint;// 公共的消息線程Thread commonReceiveThread;// 公共消息的緩存private byte[] commonMsgBuff;private void Start(){//Udp對象本身需要綁定一個端口 Any代表當前的IP地址,0代表任意一個可用的端口CommonUdpClient = new UdpClient(new IPEndPoint(IPAddress.Any,0));//公共消息接收端口 UDP對象將從這個端口接收消息commonReceiveIPEndPoint = new IPEndPoint(IPAddress.Any, 0);//啟動監聽線程,和TCP一樣,監聽會阻塞線程,不能在主線程中監聽消息commonReceiveThread = new Thread(ListenCommonMsg);commonReceiveThread.IsBackground = true;commonReceiveThread.Start();}// 監聽公共消息private void ListenCommonMsg(){while (true){//這里會阻塞線程commonMsgBuff = CommonUdpClient.Receive(ref commonReceiveIPEndPoint);string temp = Encoding.UTF8.GetString(commonMsgBuff);print("公共消息為" + temp);}}注意事項
1 監聽操作必須在子線程中執行,不要在主線程中執行,否則主線程會被阻塞
2 重復監聽消息應該使用死循環
3 子線程可以操作主線程的簡單屬性,如果 int bool str,但不能直接操作Uinty對象如Text等,如果需要在接收消息后改變場景內容 ,在一個主線程的腳本中的Update里,使用一個bool制作一把鎖,將子線程收到的消息,賦值給主線程腳本的string變量,并將鎖置為true,使用這個腳本來控制Uinty對象
例
總結
以上是生活随笔為你收集整理的untiy Socket通信一篇通的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 视频消重软件百度云 小视频修改md5
- 下一篇: 辰信领创荣获“2016中国IT风云榜”两