知識點來源:總結人間自有韜哥在, 唐老獅,豆包
目錄
- 1.網絡通信-通信必備知識-IP地址和端口類
- 2.網絡通信中序列化和反序列化2進制數據
- 3.Socket類
- 4.TCP同步服務端和客戶端基礎實現
- 4.1.服務端基本實現
- 4.2.客戶端實現:
- 5.區分消息類型
- 6.分包和粘包
- 7.TCP同步退出消息和心跳消息
- 7.1.客服端主動斷開連接
- 7.2.心跳消息
- 8.Socket類TCP異步常用成員
- 9.UDP
- 10.Socket類UDP異步常用方法
- 11.FTP
- 11.1.FTP上傳文件
- 11.2.FTP下載文件
- 12.HTTP
- 13.WWW
- 14.WWWForm
- 15.UnityWebRequest
- 15.1.常用上傳和獲取數據
- 15.2.高級上傳和獲取數據
- 16.Protobuf
- 16.1.Protobuf配置規則
- 16.2.Protobuf協議使用
- 序列化存儲為本地文件
- 反序列化本地文件
- 得到序列化后的字節數組
- 從字節數組反序列化
- 17.大小端模式
1.網絡通信-通信必備知識-IP地址和端口類
分類 | 詳情 |
---|---|
IPAddress類 | 命名空間:System.Net |
IPEndPoint類 | 命名空間:System.Net |
IPHostEntry | 作為域名解析返回值,用于獲取IP、主機名等信息。成員變量有:AddressList(獲取關聯IP)、Aliases(獲取主機別名列表)、HostName(獲取DNS名稱) |
Dns | 靜態類,提供根據域名獲取IP的方法。常用方法有:GetHostName(獲取本地主機名,如 |
2.網絡通信中序列化和反序列化2進制數據
類名 | 詳情 |
---|---|
BitConverter | 所在命名空間:System |
Encoding | 所在命名空間:System.Text |
3.Socket類
類名 | 詳情 |
---|---|
Socket | 命名空間:System.Net.Sockets |
4.TCP同步服務端和客戶端基礎實現
4.1.服務端基本實現
(1)創建套接字Socket對象(TCP):
//1.創建套接字Socket(TCP)
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2)用Bind方法將套接字與本地地址綁定:
//2.用Bind方法將套接字與本地地址綁定
try
{IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//把本機作為服務端程序 IP地址傳入本機socketTcp.Bind(iPEndPoint);//綁定
}
catch (Exception e)
{//如果IP地址不合法或者端口號被占用可能報錯Console.WriteLine("綁定報錯" + e.Message);return;
}
(3)用Listen方法監聽:
//3.用Listen方法監聽
socketTcp.Listen(1024);//最大接收1024個客戶端
Console.WriteLine("服務端綁定監聽結束,等待客戶端連入");
(4)用Accept方法等待客戶端連接,建立連接,Accept返回新套接字:
//5.建立連接,Accept返回新套接字
Socket socketClient = socketTcp.Accept();
//Accept是阻塞式的方法 會把主線程卡主 一定要等到客戶端接入后才會繼續執行后面的代碼
//客戶端接入后 返回新的Socket對象 這個新的Socket可以理解為客戶段和服務端的通信通道
Console.WriteLine("有客戶端連入了");
(5)用Send和Receive相關方法收發數據:
//6.用Send和Receive相關方法收發數據
//發送字符串轉成的字節數組給客戶端
socketClient.Send(Encoding.UTF8.GetBytes("歡迎連入服務端"));
//聲明接受客戶端信息的字節數組 聲明1024容量代表能接受1kb的信息
byte[] result = new byte[1024];
//接受客戶端信息 返回值為接受到的字節數
int receiveNum = socketClient.Receive(result);
//打印 遠程發送信息的客戶端的IP和端口 以及 發送過來的字符串
Console.WriteLine("接受到了{0}發來的消息:{1}",socketClient.RemoteEndPoint.ToString(),Encoding.UTF8.GetString(result, 0, receiveNum));
(6)用Shutdown方法釋放連接:
//7.用Shutdown方法釋放連接
//注意斷開的是客戶段和服務端的通信通道
socketClient.Shutdown(SocketShutdown.Both);
(7)關閉套接字:
//8.關閉套接字
//注意關閉的是客戶段和服務端的通信通道
socketClient.Close();
4.2.客戶端實現:
(1)創建套接字Socket Tcp
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
(2)用Connect方法與服務端相連:
//確定服務端的IP和端口 正常來說填的應該是遠端服務器的ip地址以及端口號
//由于只有一臺電腦用于測試 本機也當做服務器 所以傳入當前電腦的ip地址
IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
try
{//連接socketTcp.Connect(iPEndPoint);
}
catch (SocketException e)
{//如果連接沒有開啟或者服務器異常 會報錯 不同的返回碼代表不同報錯if (e.ErrorCode == 10061)print("服務器拒絕連接");elseprint("連接服務器失敗" + e.ErrorCode);return;
}
(3)用Send和Receive相關方法收發數據:
//接收數據
//聲明接收數據字節數組
byte[] receiveBytes = new byte[1024];
//Receive方法接受數據 返回接收多少字節
int receiveNum = socketTcp.Receive(receiveBytes);print("收到服務端發來的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));//發送數據
socketTcp.Send(Encoding.UTF8.GetBytes("你好,我是韜老獅的客戶端"));
(4)用Shutdown方法釋放連接:
//4.用Shutdown方法釋放連接
socketTcp.Shutdown(SocketShutdown.Both);
(5)關閉套接字:
//5.關閉套接字
socketTcp.Close();
5.區分消息類型
為發送的信息添加標識,比如添加消息 ID。在所有發送的消息的頭部加上消息 ID(可以是 int、short、byte、long)。
舉例說明:
如果選用 int 類型作為消息 ID 的類型,前 4 個字節為消息 ID,后面的字節為數據類的內容,這樣每次收到消息時,先把前 4 個字節取出來解析為消息 ID,再根據 ID 進行消息反序列化即可。
6.分包和粘包
什么是分包、黏包?
分包、黏包指在網絡通信中由于各種因素(網絡環境、API規則等)造成的消息與消息之間出現的兩種狀態。
分包:一個消息分成了多個消息進行發送。
黏包:一個消息和另一個消息黏在了一起。
注意:分包和黏包可能同時發生。
解決思路:為消息添加頭部記錄長度,依據長度判斷并處理分包、黏包,僅處理完整消息。
7.TCP同步退出消息和心跳消息
7.1.客服端主動斷開連接
- 解決思路及主要修改邏輯
- 客戶端:使用
Disconnect
方法主動斷開連接,在TcpNetManager
的Close
方法中,依次執行關閉套接字發送和接收、手動停止連接、關閉套接字連接、將套接字置空以及標記連接已關閉等操作。 - 服務端
- 在
ServerSocket
中添加CloseClientSocket
方法,用于關閉客戶端連接并從字典中移除,操作客戶端對象字典時添加線程鎖以保證線程安全。 - 在
ServerSocket
的其他操作或訪問字典的方法(如AcceptClientConnect
、ReceiveClientMessage
、Broadcast
)中添加線程鎖,確保字典操作的線程安全性。 - 為避免在遍歷字典進行消息收發操作時直接移除客戶端導致報錯,在
ServerSocket
中創建List
存儲待移除的客戶端socket(delList
),并定義AddDelSocket
方法用于添加待移除的socket。 - 將
Program
的serverSocket
改為靜態變量,在ClientSocket
的發送和接收消息方法中,檢測到斷開連接或解析報錯時,將當前客戶端添加到服務端待移除的客戶端列表中。
- 在
- 客戶端:使用
7.2.心跳消息
什么是心跳消息
所謂心跳消息,就是在長連接中,客戶端和服務端之間定期發送的一種特殊的數據包,用于通知對方自己還在線,以確保長連接的有效性。由于其發送的時間間隔往往是固定的持續的,就像是心跳一樣一直存在,所以我們稱之為心跳消息。
為什么需要心跳消息
避免非正常關閉客戶端時,服務器無法正常收到關閉連接消息。通過心跳消息我們可以自定義超時判斷,如果超時沒有收到客戶端消息,證明客戶端已經斷開連接。避免客戶端長期不發送消息,防火墻或者路由器會斷開連接,我們可以通過心跳消息一直保持活躍狀態。
實現心跳消息
客戶端:主要功能是定時發送消息。
服務器端:主要功能是不停檢測上次收到某客戶端消息的時間,如果超時則認為連接已經斷開。
8.Socket類TCP異步常用成員
類名 | 方法 | 參數 | 詳情 |
---|---|---|---|
Socket | BeginAccept(AsyncCallback callback, object state) | callback :處理異步接受客戶端連接操作完成時的回調函數;state :服務端Socket 對象 | 服務端開始接收客戶端的連接。 |
Socket | EndAccept(IAsyncResult asyncResult) | asyncResult :BeginAccept 方法返回的IAsyncResult 對象 | 服務端檢測到客戶端的連接結束,得到客戶端Socket 。 |
Socket | BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) | remoteEP :服務器的終結點(如IPEndPoint );callback :處理客戶端異步連接操作完成時的回調函數;state :客戶端的Socket 對象 | 客戶端異步連接到服務器。 |
Socket | EndConnect(IAsyncResult asyncResult) | asyncResult :BeginConnect 方法返回的IAsyncResult 對象 | 客戶端完成異步連接到服務器端的操作。 |
Socket | BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) | buffer :接收消息的字節數組;offset :從接收消息的字節數組第幾位開始存儲;size :字節數組長度;socketFlags :Socket 標識;callback :接收消息回調函數;state :Socket 對象 | 開始接收消息。 |
Socket | EndReceive(IAsyncResult asyncResult) | asyncResult :BeginReceive 方法返回的IAsyncResult 對象 | 結束接收消息,返回接收的字節數組長度。 |
Socket | BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) | buffer :發送消息的字節數組;offset :從發送消息的字節數組第幾位開始發送;size :字節數組長度;socketFlags :Socket 標識;callback :發送消息回調函數;state :Socket 對象 | 開始發送消息。 |
Socket | EndSend(IAsyncResult asyncResult) | asyncResult :BeginSend 方法返回的IAsyncResult 對象 | 結束發送消息,返回成功發送的字節數。 |
Socket | AcceptAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,用于處理異步接受客戶端連接操作完成后的操作 | 服務端異步接受客戶端連接。 |
Socket | ConnectAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,用于處理異步連接到服務器操作完成后的操作 | 客戶端異步連接到服務器。 |
Socket | SendAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,設置好發送數據的緩沖區等信息,用于處理異步發送消息操作完成后的操作 | 異步發送消息。 |
Socket | ReceiveAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,設置好接收數據的緩沖區等信息,用于處理異步接受消息操作完成后的操作 | 異步接受消息。 |
9.UDP
實現UDP客戶端通信收發字符串
//1.創建套接字 尋址類型還是Ipv4 Soket類型使用數據包 協議選擇Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//2.綁定本機地址 注意模擬測試時客戶端本機和服務端遠程機的端口號不能一樣
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.Bind(ipPoint);//3.發送到指定目標 注意模擬測試時客戶端本機和服務端遠程機的端口號不能一樣
IPEndPoint remoteIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
//指定要發送的字節數 和 遠程計算機的 IP和端口
socket.SendTo(Encoding.UTF8.GetBytes("韜老獅來了"), remoteIpPoint);//4.接受消息
//裝接收消息的字節數組
byte[] bytes = new byte[512];
//裝接收消息的IP地址和端口的對象 在ReceiveFrom方法會使用ref賦值 主要是用來記錄 誰發的信息給你 傳入函數后 在內部 它會幫助我們進行賦值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
//接收消息 得到消息長度
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
print("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +"port:" + (remoteIpPoint2 as IPEndPoint).Port +"發來了" +Encoding.UTF8.GetString(bytes, 0, length));//5.釋放關閉
socket.Shutdown(SocketShutdown.Both);
socket.Close();
實現UDP服務端通信收發字符串
//1.創建套接字 尋址類型還是Ipv4 Soket類型使用數據包 協議選擇Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);//2.綁定本機地址 注意模擬測試時客戶端本機和服務端遠程機的端口號不能一樣
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(ipPoint);
Console.WriteLine("服務器開啟");//3.接受消息
//裝接收消息的字節數組
byte[] bytes = new byte[512];
//裝接收消息的IP地址和端口的對象 在ReceiveFrom方法會使用ref賦值 主要是用來記錄 誰發的信息給你 傳入函數后 在內部 它會幫助我們進行賦值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
Console.WriteLine("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +"port:" + (remoteIpPoint2 as IPEndPoint).Port +"發來了" +Encoding.UTF8.GetString(bytes, 0, length));//4.發送到指定目標
//由于服務端先收了消息 所以服務端已經知道誰發了消息給服務端 是使用remoteIpPoint2記錄的 服務端直接發給它就行了
socket.SendTo(Encoding.UTF8.GetBytes("歡迎發送消息給服務器"), remoteIpPoint2);//5.釋放關閉
socket.Shutdown(SocketShutdown.Both);
socket.Close();
10.Socket類UDP異步常用方法
類名 | 方法 | 參數 | 詳情 |
---|---|---|---|
Socket | BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state) | buffer :要發送的數據字節數組;offset :從字節數組的哪個位置開始發送;size :發送數據的長度;socketFlags :SocketFlags 標識;remoteEP :目標IP和端口號;callback :回調函數;state :回調函數的參數 | 開始向指定IP和端口異步發送數據,將結果異步傳回回調函數進行處理 |
Socket | EndSendTo(IAsyncResult asyncResult) | asyncResult :BeginSendTo 方法返回的IAsyncResult 對象 | 結束異步發送操作 |
Socket | BeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state) | buffer :緩存區;offset :緩存區的起始位置;size :最大接收數據長度;socketFlags :SocketFlags 標識;remoteEP :接收數據的來源IPEndPoint ;callback :回調函數;state :回調函數的參數 | 開始異步從UDP客戶端接收數據,將接收到的數據放入緩存區,接收數據的來源ip和端口號會被保存,有數據到達時調用回調函數處理 |
Socket | EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint remoteEP) | asyncResult :BeginReceiveFrom 方法返回的IAsyncResult 對象;remoteEP :接收數據的來源IPEndPoint | 結束異步接收操作,并返回接收到的字節數 |
Socket | SendToAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,設置好要發送的數據、目標IP地址等信息 | 開始向指定IP和端口發送數據,發送完成后在回調函數處理 |
Socket | ReceiveFromAsync(SocketAsyncEventArgs e) | e :SocketAsyncEventArgs 對象,設置好接收數據的緩沖區、接收來源的IPEndPoint 等信息 | 開始異步從UDP客戶端接收數據,接收完成后在回調函數進行處理 |
11.FTP
類名 | 用途/功能 | 重要方法 | 重要成員 |
---|---|---|---|
NetworkCredential (System.Net ) | 在網絡身份驗證中存儲用戶名和密碼信息,用于 FTP 文件傳輸場景下提供訪問 FTP 服務器所需的認證憑據 | 無 | 無 |
FtpWebRequest (System.Net ) | 用于執行上傳、下載、刪除 FTP 服務器上的文件等操作 | 1. Create :創建新 Ftp 請求。2. Abort :終止 Ftp 傳輸。3. GetRequestStream :獲取用于上傳的流。4. GetResponse :返回 FTP 服務器響應。 | 1. Credentials :設置通信憑證為NetworkCredential 對象。2. KeepAlive :設置完成請求時是否關閉到 FTP 服務器的控制連接。3. Method :設置 FTP 請求操作命令,如刪除、下載、列目錄等。4. UseBinary :指定是否采用二進制模式傳輸數據。5. RenameTo :重命名文件。 |
FtpWebResponse (System.Net ) | 封裝 FTP 服務器對請求的響應,提供操作狀態及從服務器下載的數據,用于表示響應信息,含響應代碼、消息等 | 1. Close :釋放所有資源。2. GetResponseStream :返回從 FTP 服務器下載數據的流。 | 1. ContentLength :接收到數據的長度。2. ContentType :接收數據的類型。3. StatusCode :FTP 服務器下發的最新狀態碼。4. StatusDescription :狀態代碼的文本描述。5. BannerMessage :登錄前建立連接時服務器發送的消息。6. ExitMessage :獲取 FTP 會話結束時服務器發送的消息。7. LastModified :獲取 FTP 服務器上文件的最后修改日期和時間。 |
11.1.FTP上傳文件
try
{// 創建一個Ftp連接,pic.png代表想上傳名叫pic的png圖片。// 這里的ftp://127.0.0.1是使用本機開啟服務器進行測試,實際使用時應該是遠端服務器IP。FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;// 設置通信憑證(如果不支持匿名,就必須設置這一步)。// 將代理相關信息置空,避免服務器同時有http相關服務造成沖突。ftpWebRequest.Proxy = null;// 創建并設置通信憑證。NetworkCredential networkCredential = new NetworkCredential("MrTao", "MrTao");ftpWebRequest.Credentials = networkCredential;// 請求完畢后是否關閉控制連接,如果想要關閉,可以設置為false。ftpWebRequest.KeepAlive = false;// 設置操作命令。ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;//設置命令操作為上傳文件。// 指定傳輸類型,使用二進制。ftpWebRequest.UseBinary = true;// 得到用于上傳的流對象。Stream uploadRequestStream = ftpWebRequest.GetRequestStream();// 開始上傳,使用流讀取StreamingAssets文件夾下的名叫test的圖片。using (FileStream fileStream = File.OpenRead(Application.streamingAssetsPath + "/test.png")){// 我們可以一點一點的把這個文件中的字節數組讀取出來,然后存入到上傳流中。byte[] bytes = new byte[1024];// 返回值是真正從文件中讀了多少個字節。int contentLength = fileStream.Read(bytes, 0, bytes.Length);// 不停的去讀取文件中的字節,除非讀取完畢了,不然一直讀,并且寫入到上傳流中。while (contentLength != 0){// 寫入上傳流中。uploadRequestStream.Write(bytes, 0, contentLength);// 寫完了繼續讀。contentLength = fileStream.Read(bytes, 0, bytes.Length);}// 出了循環就證明寫完了。fileStream.Close();uploadRequestStream.Close();// 上傳完畢。print("上傳結束");}
}
catch (Exception e)
{print("上傳出錯,失敗" + e.Message);
}
11.2.FTP下載文件
try
{// 創建一個Ftp連接。// 這里和上傳不同,上傳的文件名是自己定義的,下載的文件名一定是資源服務器上有的,比如一張叫pic的圖片。FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;// 設置通信憑證(如果不支持匿名,就必須設置這一步)。ftpWebRequest.Credentials = new NetworkCredential("MrTao", "MrTao");// 請求完畢后是否關閉控制連接,如果要進行多次操作,可以設置為false。ftpWebRequest.KeepAlive = false;// 設置操作命令。ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;// 指定傳輸類型。ftpWebRequest.UseBinary = true;// 代理設置為空。ftpWebRequest.Proxy = null;// 得到用于下載的流對象。// 相當于把請求發送給FTP服務器,返回值就會攜帶我們想要的信息。FtpWebResponse ftpWebResponse = ftpWebRequest.GetResponse() as FtpWebResponse;// 這就是下載的流。Stream downloadResponseStream = ftpWebResponse.GetResponseStream();// 開始下載。print(Application.persistentDataPath);using (FileStream fileStream = File.Create(Application.persistentDataPath + "/pic2.png")){// 讀取流的字節數組。byte[] bytes = new byte[1024];// 讀取下載下來的流數據。int contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);// 一點一點的下載到本地流中。while (contentLength != 0){// 把讀取出來的字節數組寫入到本地文件流中。fileStream.Write(bytes, 0, contentLength);// 繼續讀。contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);}// 下載結束,關閉流。downloadResponseStream.Close();fileStream.Close();}print("下載結束");
}
catch (Exception e)
{print("下載出錯" + e.Message);
}
12.HTTP
類名(命名空間) | 用途 | 重要方法 | 重要成員 |
---|---|---|---|
HttpWebRequest(System.Net) | 用于向服務器發送HTTP客戶端請求,進行消息通信、上傳、下載等操作 | 1. Create:創建新的WebRequest,用于HTTP相關操作 2. Abort:終止文件傳輸 3. GetRequestStream:獲取用于上傳的流 4. GetResponse:返回HTTP服務器響應 5. Begin/EndGetRequestStream:異步獲取用于上傳的流 6. Begin/EndGetResponse:異步獲取返回的HTTP服務器響應 | 1. Credentials:通信憑證,設置為NetworkCredential對象 2. PreAuthenticate:是否隨請求發送一個身份驗證標頭,一般需身份驗證時設為true 3. Headers:構成標頭的名稱/值對的集合 4. ContentLength:發送信息的字節數,上傳信息時需先設置 5. ContentType:POST請求時,需對發送內容進行內容類型設置 6. Method:操作命令設置,如Get、Post、Head等 |
HttpWebResponse(System.Net) | 用于獲取服務器反饋信息,通過HttpWebRequest對象的GetResponse()方法獲取,使用完畢用Close釋放 | 1. Close:釋放所有資源 2. GetResponseStream:返回從FTP服務器下載數據的流 | 1. ContentLength:接受到數據的長度 2. ContentType:接受數據的類型 3. StatusCode:HTTP服務器下發的最新狀態碼 4. StatusDescription:HTTP服務器下發的狀態代碼的文本 5. BannerMessage:登錄前建立連接時HTTP服務器發送的消息 6. ExitMessage:HTTP會話結束時服務器發送的消息 7. LastModified:HTTP服務器上的文件的上次修改日期和時間 |
NetworkCredential、Uri、Stream、FileStream(相關命名空間) | 在HTTP通訊時使用方式與FTP類似 | - | - |
13.WWW
類名(命名空間) | 作用 | 常用方法 | 常用變量 |
---|---|---|---|
WWW(UnityEngine) | 用于簡單訪問網頁,可進行數據的下載和上傳。使用 HTTP 協議時默認請求類型是 Get,進行 Post 上傳需配合 WWWForm 類 | 1. WWW:構造函數,用于創建一個 WWW 請求 2. GetAudioClip:從下載數據返回一個音效切片 AudioClip 對象 3. LoadImageIntoTexture:用下載數據中的圖像來替換現有的一個 Texture2D 對象 4. LoadFromCacheOrDownload:從緩存加載 AB 包對象,如果該包不在緩存則自動下載存儲到緩存中,以便以后直接從本地緩存中加載 | 1. assetBundle:如果加載的數據是 AssetBundle,則可直接獲取加載結果 2. audioClip:如果加載的數據是音效切片文件,可直接獲取加載結果(新版本用 GetAudioClip 方法) 3. bytes:以字節數組形式獲取加載內容 4. bytesDownloaded:已下載的字節數 5. error:下載出錯時返回錯誤消息 6. isDone:判斷下載是否完成 7. movie:下載視頻時可獲取 MovieTexture 類型結果(新版本用 GetMovieTexture 方法) 8. progress:下載進度 9. text:下載數據為字符串時,以字符串形式返回內容 10. texture:下載數據為圖片時,以 Texture2D 形式返回加載結果 |
14.WWWForm
類名 | 作用 | 注意事項 | 常用方法 |
---|---|---|---|
WWWForm | 在使用 WWW 類下載數據的基礎上,若需上傳數據,可結合該類使用。主要用于集成數據,可設置上傳的參數或者二進制數據,結合使用時主要用 Post 請求類型,通過 Http 協議上傳處理 | 使用 WWW 結合 WWWForm 上傳數據通常需要與后端程序制定上傳規則 | 1. WWWForm:構造函數 2. AddBinaryData:添加二進制數據 3. AddField:添加字段 |
15.UnityWebRequest
15.1.常用上傳和獲取數據
獲取
類名 | 方法 | 方法解析 | 參數 |
---|---|---|---|
UnityWebRequest | Get | 創建一個用于獲取文本或二進制數據的 UnityWebRequest 對象。 | uri :請求資源的 URI 地址,如 "http://192.168.1.101:8000/HTTPRoot/test.txt" |
UnityWebRequestTexture | GetTexture | 創建一個用于獲取紋理數據的 UnityWebRequest 對象。 | uri :請求紋理資源的 URI 地址,可以是 http 、ftp 或 file 協議的地址,如 "http://192.168.1.101:8000/HTTPRoot/pic.png" 、"ftp://127.0.0.1/pic.png" 或 "file://" + Application.streamingAssetsPath + "/test.png" |
UnityWebRequestAssetBundle | GetAssetBundle | 創建一個用于獲取 AB 包數據的 UnityWebRequest 對象。 | uri :請求 AB 包資源的 URI 地址,如 "http://192.168.1.101:8000/HTTPRoot/model" |
UnityWebRequest | SendWebRequest | 發送 Web 請求并等待服務器響應。 | 無 |
DownloadHandlerTexture | GetContent | 從 UnityWebRequest 對象中獲取下載的紋理數據。 | request :UnityWebRequest 對象,用于獲取其中的紋理數據 |
DownloadHandlerAssetBundle | GetContent | 從 UnityWebRequest 對象中獲取下載的 AB 包數據。 | request :UnityWebRequest 對象,用于獲取其中的 AB 包數據 |
上傳
類名/接口 | 方法/描述 | 參數 |
---|---|---|
IMultipartFormSection | 數據相關類繼承的父接口,可使用父類類型的集合來存儲子類對象 | 無 |
MultipartFormDataSection | 用于傳遞鍵值對數據的類 | 1. 構造函數(二進制字節數組): |
MultipartFormFileSection | 用于傳遞文件數據的類 | 1. 構造函數(字節數組): |
UnityWebRequest | Post 方法:創建一個用于使用 POST 請求上傳數據的UnityWebRequest 對象Put 方法:創建一個用于使用 PUT 請求上傳數據的UnityWebRequest 對象SendWebRequest 方法:發送 Web 請求并等待服務器響應 | 1. |
NetWWWManager | UploadFile 方法:啟動異步上傳文件的操作UploadFileAsync 方法:異步上傳文件的具體實現 | 1. |
15.2.高級上傳和獲取數據
獲取
類名 | 方法/屬性 | 方法的參數 |
---|---|---|
UnityWebRequest | 構造函數 | 無 |
UnityWebRequest | url | 字符串類型的服務器地址 |
UnityWebRequest | method | 如UnityWebRequest.kHttpVerbPOST等請求類型常量 |
UnityWebRequest | timeout | 整數類型的超時時間(單位:毫秒) |
UnityWebRequest | redirectLimit | 整數類型,0表示不進行重定向 |
UnityWebRequest | responseCode | 無 |
UnityWebRequest | result | 無 |
UnityWebRequest | error | 無 |
UnityWebRequest | downloadHandler | 實現了DownloadHandler的對象,如DownloadHandlerBuffer等 |
UnityWebRequest | uploadHandler | 實現了UploadHandler的對象 |
UnityWebRequest | Get | 字符串類型的請求URL |
UnityWebRequest | GetTexture | 字符串類型的紋理URL |
UnityWebRequestAssetBundle | GetAssetBundle | 字符串類型的AssetBundle URL |
UnityWebRequest | Put | 字符串類型的請求URL和字節數組形式的上傳內容 |
UnityWebRequest | Post | 字符串類型的請求URL和List類型的上傳數據列表 |
UnityWebRequest | isDone | 無 |
UnityWebRequest | downloadProgress | 無 |
UnityWebRequest | downloadedBytes | 無 |
UnityWebRequest | uploadProgress | 無 |
UnityWebRequest | uploadedBytes | 無 |
UnityWebRequest | SendWebRequest | 無 |
DownloadHandlerBuffer | 構造函數 | 無 |
DownloadHandlerFile | 構造函數 | 字符串類型的本地文件保存路徑 |
DownloadHandlerTexture | 構造函數 | 無 |
DownloadHandlerAssetBundle | 構造函數 | 字符串類型的請求URL和校驗碼(ulong類型,不知道則傳入0) |
UnityWebRequestMultimedia | GetAudioClip | 字符串類型的音頻文件URL和AudioType類型的音頻類型 |
DownloadHandlerAudioClip | GetContent | UnityWebRequest對象 |
CustomDownLoadFileHandler | 構造函數(無參) | 無 |
CustomDownLoadFileHandler | 構造函數(字節數組) | 字節數組類型的參數 |
CustomDownLoadFileHandler | 構造函數(路徑) | 字符串類型的本地存儲路徑 |
CustomDownLoadFileHandler | GetData | 無 |
CustomDownLoadFileHandler | ReceiveData | byte[]類型的數據和int類型的數據長度 |
CustomDownLoadFileHandler | ReceiveContentLengthHeader | ulong類型的內容長度 |
CustomDownLoadFileHandler | CompleteContent | 無 |
上傳
類名 | 方法/屬性 | 方法的參數 | 描述 |
---|---|---|---|
UnityWebRequest | 構造函數 | string uri , string method :請求的 URL 和 HTTP 請求方法(如 UnityWebRequest.kHttpVerbPOST ) | 創建一個 UnityWebRequest 對象,用于網絡請求 |
UnityWebRequest | uploadHandler | 實現了 UploadHandler 的對象,如 UploadHandlerRaw 、UploadHandlerFile 等 | 設置上傳處理對象,用于處理上傳的數據 |
UnityWebRequest | SendWebRequest | 無 | 發送網絡請求,并等待請求返回 |
UnityWebRequest | result | 無 | 獲取網絡請求的結果(成功或失敗) |
UploadHandlerRaw | 構造函數 | byte[] data :要上傳的字節數組 | 創建一個用于上傳字節數組的 UploadHandlerRaw 對象 |
UploadHandlerRaw | contentType | string 類型的內容類型,如 "類型/細分類型" | 設置上傳數據的內容類型,若不設置,默認是 application/octet - stream |
UploadHandlerFile | 構造函數 | string path :要上傳的文件路徑 | 創建一個用于上傳文件的 UploadHandlerFile 對象 |
16.Protobuf
16.1.Protobuf配置規則
(1)配置文件后綴:統一使用.proto
,可通過多個該后綴文件配置。
(2)版本號:第一行需指定,如syntax = "proto3";
,不寫默認用proto2。
(3)注釋方式:支持//
單行注釋和/* */
多行注釋。
(4)命名空間:用package
指定,如package 命名空間名;
。
(5)消息類:使用message
定義,格式為message 類名 { // 字段聲明 }
。
(6)成員類型和編號:成員類型包含浮點數(float
、double
)、整數(int32
、int64
等)、其他(bool
、string
、bytes
),每個成員需指定從1開始的唯一編號。
(7)特殊標識:optional
表示可選賦值字段;repeated
表示數組(required
在proto3中已移除 )。
(8)枚舉:用enum
定義,首個常量必須為0,如enum 枚舉名 { 常量1 = 0; 常量2 = 1; }
。
(9)默認值:string
為空字符串、bytes
為空字節、bool
為false
、數值為0、枚舉為0、message
在C#中為空。
(10)嵌套:支持消息類和枚舉的嵌套定義。
(11)保留字段:使用reserved
關鍵字保留字段,防止已刪編號被重新使用。
(12)導入定義:若使用其他配置中的類型,用import "配置文件路徑";
導入。
syntax = "proto3"; // 決定了 proto 文檔的版本號// 規則1:版本號// 規則2:注釋方式
// 注釋方式一
/* 注釋方式二 */// 規則11:導入定義
// 兩個配置在同一路徑可以這樣寫,在不同路徑要包含文件夾路徑
import "test2.proto";// 規則3:命名空間
package GamePlayerTest; // 這決定了命名空間// 規則4:消息類
message TestMsg {// 規則5:成員類型 和 唯一編號// 浮點數// = 1 不代表默認值,而是代表唯一編號,方便我們進行序列化和反序列化的處理// 規則6:特殊標識// required: 必須賦值的字段required float testF = 1; // C# - float// optional: 可以不賦值的字段optional double testD = 2; // C# - double// 變長編碼// 所謂變長,就是會根據數字的大小來使用對應的字節數來存儲,1 2 4// Protobuf 幫助我們優化的部分,可以盡量少的使用字節數來存儲內容int32 testInt32 = 3; // C# - int 它不太適用于來表示負數,請使用 sint32// 1 2 4 8int64 testInt64 = 4; // C# - long 它不太適用于來表示負數,請使用 sint64// 更實用與表示負數類型的整數sint32 testSInt32 = 5; // C# - int 適用于來表示負數的整數sint64 testSInt64 = 6; // C# - long 適用于來表示負數的整數// 無符號 變長編碼// 1 2 4uint32 testUInt = 7; // C# - uint 變長的編碼uint64 testULong = 8; // C# - ulong 變長的編碼// 固定字節數的類型fixed32 testFixed32 = 9; // C# - uint 它通常用來表示大于2的28次方的數,比 uint32 更有效,始終是4個字節fixed64 testFixed64 = 10; // C# - ulong 它通常用來表示大于2的56次方的數,比 uint64 更有效,始終是8個字節sfixed32 testSFixed32 = 11; // C# - int 始終4個字節sfixed64 testSFixed64 = 12; // C# - long 始終8個字節// 其它類型bool testBool = 13; // C# - boolstring testStr = 14; // C# - stringbytes testBytes = 15; // C# - BytesString 字節字符串// 數組 Listrepeated int32 listInt = 16; // C# - 類似 List<int> 的使用// 字典 Dictionarymap<int32, string> testMap = 17; // C# - 類似 Dictionary<int, string> 的使用// 規則7:枚舉// 枚舉成員變量的聲明需要唯一編碼TestEnum testEnum = 18;// 規則8:默認值// 聲明自定義類對象 需要唯一編碼// 默認值是 nullTestMsg2 testMsg2 = 19;// 規則9:允許嵌套// 嵌套一個類在另一個類當中,相當于是內部類message TestMsg3 {int32 testInt32 = 1;}TestMsg3 testMsg3 = 20;// 規則9:允許嵌套enum TestEnum2 {NORMAL = 0; // 第一個常量必須映射到0BOSS = 1;}TestEnum2 testEnum2 = 21;// 規則10:保留字段// int32 testInt3233333 = 22;bool testBool2123123 = 23;// 告訴編譯器 22 被占用,不準用戶使用// 之所以有這個功能,是為了在版本不匹配時,反序列化時,不會出現結構不統一,解析錯誤的問題reserved 22;reserved testInt3233333;// 規則11:導入定義GameSystemTest.HeartMsg testHeart = 24;
}// 規則7:枚舉的聲明
enum TestEnum {NORMAL = 0; // 第一個常量必須映射到0BOSS = 5;
}// 規則8:默認值
message TestMsg2 {int32 testInt32 = 1;
}syntax = "proto3"; // 決定了 proto 文檔的版本號
package GameSystemTest; // 這決定了命名空間message HeartMsg {int64 time = 1;
}
16.2.Protobuf協議使用
序列化存儲為本地文件
- 主要使用方法:生成的類中的
WriteTo
方法和文件流FileStream
對象。 - 示例代碼:
TestMsg testMsg1 = new TestMsg();
testMsg1.ListInt.Add(1);
testMsg1.TestBool = false;
testMsg1.TestD = 5.5;
testMsg1.TestInt32 = 99;
testMsg1.TestMap.Add(1, "楊");
testMsg1.TestMsg2 = new TestMsg2();
testMsg1.TestMsg2.TestInt32 = 88;
testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
testMsg1.TestMsg3.TestInt32 = 66;testMsg1.TestHeart = new GameSystemTest.HeartMsg();
testMsg1.TestHeart.Time = 7777;print(Application.persistentDataPath);
using (FileStream fileStream = File.Create(Application.persistentDataPath + "/TestMsg.tao"))
{testMsg1.WriteTo(fileStream);
}
- 代碼解釋:先創建
TestMsg
實例并設置其屬性值,然后使用File.Create
方法創建一個文件流,將TestMsg
實例序列化后寫入該文件流,實現數據的本地存儲。
反序列化本地文件
- 主要使用方法:生成的類中的
Parser.ParseFrom
方法和文件流FileStream
對象。 - 示例代碼:
using (FileStream fileStream = File.OpenRead(Application.persistentDataPath + "/TestMsg.tao"))
{TestMsg testMsg2 = null;testMsg2 = TestMsg.Parser.ParseFrom(fileStream);print(testMsg2.TestMap[1]);print(testMsg2.ListInt[0]);print(testMsg2.TestD);print(testMsg2.TestMsg2.TestInt32);print(testMsg2.TestMsg3.TestInt32);print(testMsg2.TestHeart.Time);
}
- 代碼解釋:使用
File.OpenRead
方法打開之前存儲的文件流,然后調用Parser.ParseFrom
方法從文件流中反序列化出TestMsg
實例,最后打印該實例的屬性值。
得到序列化后的字節數組
- 主要使用方法:生成的類中的
WriteTo
方法和內存流MemoryStream
對象。 - 示例代碼:
byte[] bytes = null;
using (MemoryStream memoryStream = new MemoryStream())
{testMsg1.WriteTo(memoryStream);bytes = memoryStream.ToArray();print("字節數組長度" + bytes.Length);
}
- 代碼解釋:創建一個內存流,將
TestMsg
實例序列化后寫入該內存流,再通過ToArray
方法將內存流中的數據轉換為字節數組,方便進行網絡傳輸。
從字節數組反序列化
- 主要使用方法:生成的類中的
Parser.ParseFrom
方法和內存流MemoryStream
對象。 - 示例代碼:
using (MemoryStream memoryStream = new MemoryStream(bytes))
{print("內存流當中反序列化的內容");TestMsg testMsg2 = TestMsg.Parser.ParseFrom(memoryStream);print(testMsg2.TestMap[1]);print(testMsg2.ListInt[0]);print(testMsg2.TestD);print(testMsg2.TestMsg2.TestInt32);print(testMsg2.TestMsg3.TestInt32);print(testMsg2.TestHeart.Time);
}
- 代碼解釋:使用包含字節數組的內存流,調用
Parser.ParseFrom
方法從內存流中反序列化出TestMsg
實例,最后打印該實例的屬性值。
17.大小端模式
(1)大小端模式的基本概念
- 大端模式:數據的高字節保存在內存的低地址中,低字節保存在內存的高地址中。地址由小向大增加時,數據從高位往低位放置,類似字符串順序處理,符合人類閱讀習慣。
- 小端模式:數據的高字節保存在內存的高地址中,低字節保存在內存的低地址中。例如十六進制數據 0x11223344,大端模式存儲為 11 22 33 44(地址 0 1 2 3),小端模式存儲為 44 33 22 11(地址 0 1 2 3)。
- 存在原因:是計算機硬件的兩種存儲數據方式(字節序)。計算機內部處理按順序讀取字節,因處理低位字節效率高而采用小端模式,人類讀寫習慣是大端字節序,網絡傳輸和文件存儲等場合一般用大端模式。
(2)大小端模式的影響及轉換
- 影響:只有讀取數據時需區分大小端字節序。網絡傳輸中,前后端語言和設備差異可能致大小端不統一,如 C# 和 Java/Erlang/AS3 通訊需轉換,C# 與 C++ 通信通常無需特殊處理。
- 轉換方法
- 判斷模式:用
BitConverter.IsLittleEndian
判斷,如print("是否是小端模式:" + BitConverter.IsLittleEndian);
。 - 轉為大端模式:通過
IPAddress.HostToNetworkOrder
將本機字節序轉網絡字節序(即大端模式),如int i = 99; byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(i));
。 - 轉為小端模式:用
IPAddress.NetworkToHostOrder
將網絡字節序轉本機字節序(即小端模式),如int receI = BitConverter.ToInt32(bytes, 0); receI = IPAddress.NetworkToHostOrder(receI);
。 - 倒序數組轉換:使用
Array.Reverse
倒序數組,若后端需大端模式且當前是小端模式則轉換,如if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
。
- 判斷模式:用