日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

untiy Socket通信一篇通

發布時間:2024/1/8 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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

//TCP連接的監聽器private TcpListener tcpListener;//TCP的連接監聽線程private Thread tcpListenerT;//連接后的TCP對象 private TcpClient client;private void Awake(){//開啟連接監聽的線程,必須另開一個線程啟動監聽,如果在主線程里直接調用,主線程會阻塞tcpListenerT = new Thread(ListenConnect);tcpListenerT.IsBackground = true;tcpListenerT.Start();}//監聽連接請求的方法private void ListenConnect(){//參數為連接監聽器綁定的一個IP端口,其余的連接者必須連接這個IP端口tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 10000)); tcpListener.Start(0);//參數為同時可以處理的連接請求數 0代表無限制while (true)//使用死循環來重復監聽連接,可以將接收的tcpclient放入一個集合里{//這一步會阻塞線程,不能在主線程中調用client = tcpListener.AcceptTcpClient();//當獲取到tcpClient對象后,就可以通過這個對象收發消息//如果需要,可以這樣獲取客戶端的IP 端口string ip = (client.Client.RemoteEndPoint as IPEndPoint).Address.ToString();string prot = (client.Client.RemoteEndPoint as IPEndPoint).Port.ToString();}}

連接端:

private TcpClient tcpClient = new TcpClient();//連上后,直接用這個對象和連接端通信private void Start(){//參數為服務器的地址,和服務器的TcpListener綁定的端口,注意tcpClient本身不需要指定IP端口,會自動分配tcpClient.Connect("192.168.1.36", 10000);//注意這也是一個阻塞方法,如果連不上服務器會導致程序卡住,自己開個連接線程}

以下為通信的方法:
發送消息:

public void SendMsg(TcpClient client,string msg){byte[] buff = Encoding.UTF8.GetBytes(msg);client.Send(buff);}

接收消息:

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;TcpListener tcpListener;private void Start(){tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any,1000));tcpListener.Start();//開始異步接受連接請求,會自動創建接受連接請求tcpListener.BeginAcceptTcpClient(AcceptCallBack, tcpListener);}//接收到連接請求的回調函數private void AcceptCallBack(IAsyncResult ar){try{TcpListener listener = ar.AsyncState as TcpListener;tcpClient = listener.EndAcceptTcpClient(ar);Debug.Log("已連接");//開啟下一個監聽連接tcpListener.BeginAcceptTcpClient(AcceptCallBack, tcpListener);}catch(Exception e){Debug.LogError("連接錯誤"+e.ToString());}}

連接端:

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對象

//子線程接到消息后,置為truepublic bool isReceive = false;/// <summary>/// 子線程接到消息后,將消息賦值到這個變量/// </summary>public string msg;void Update(){if (isReceive){//處理msgif(msg == "1"){//邏輯}}}

總結

以上是生活随笔為你收集整理的untiy Socket通信一篇通的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。