Unity如何實現網路通訊(二) SOCKET

2021-08-09 07:22:45 字數 3175 閱讀 5812

強聯網在我們的遊戲開發中所佔比重越來越大,尤其是開發mmo遊戲時,更需要強聯網來進行實時更新,所以我們就有了強聯網的需要。

首先我們得清楚強聯網的工作原理,說到強聯網,我們就必須說到socket。

socket是對tcp/ip協議的封裝和應用,是面向程式設計師的,給我們提供了操作網路的介面,但是我們也必須基本了解其工作原理:

強聯網我們主要使用的是tcp和udp,首先我們說一下tcp。

一說到tcp,必然會想到三次握手和四次揮手,建立連線和斷開連線的原理雖然在程式設計過程中不會涉及太多,但還是有了解的必要。

三次握手:客戶端和服務端建立連線需要三次握手

第一次:客戶端向服務端傳送報文,向伺服器傳送連線請求;

第二次:服務端向客戶端返回ack報文,通知客戶端可以連線;

第三次:客戶端收到服務端報文,正式連線服務端。

三次握手完成。

四次揮手:客戶端要與伺服器斷開連線,需要四次揮手

第一次:客戶端向服務端傳送fin報文,向伺服器傳送中斷連線請求;

第二次:伺服器收到客戶端中斷請求,向伺服器傳送已得知中斷請求,但伺服器還有資源未處理,需要等待;

第三次:伺服器處理完資料後,再次向客戶端傳送報文,告訴客戶端可以斷開連線了;

第四次:客戶端收到服務端斷開連線的確認資訊後,最後傳送資訊看是否真的斷開連線了,如果伺服器沒有一段時間沒有回應,則說明已經斷開,中斷過程完成;

四次揮手完成。

那麼我們如何使用強聯網呢?

在c#中我們可以通過使用socket來進行連線:

在服務端的流程:

建立socket->繫結ip埠->設定排隊連線請求數量->啟動監聽->收發訊息->關閉

在客戶端的流程:

建立socket->連線對應ip埠->開始收發->關閉

我們會發現,服務端的流程比客戶端長,那是因為客戶端只需要連線乙個伺服器就好了,而服務端要接收不同的客戶端。

具體socket介面的使用在網上都有教程,在這裡不做多述,只需要注意一點就是選擇tcp,udp使用不同的引數:

serversocket =

newsocket

(addressfamily

.internetwork,

sockettype

.stream,

protocoltype

.tcp);

這個是tcp的連線,三個引數分別是位址簇(ipv4),傳輸模式(流模式),協議(tcp)

而如果我們要udp連線,則要如下:

serversocket =

newsocket

(addressfamily

.internetwork,

sockettype

.dgram,

protocoltype

.udp);

udp所需要的傳輸模式是資料報模式,所以我們在改變協議的同時,也要記得改變傳輸型別。

接下來就進入了我們的正題,有一定開發經驗的遊戲開發者一定知道,在網路傳輸資料的過程中會出現分包粘包的現象,我們首先來說一下什麼叫分包、粘包。

分包:傳輸資料不完整,一條資訊被分成多次傳送。

比如我們傳送了一條資訊:「你好」,如果分包現象發生,我們可能只收到了「你」,卻沒有收到「好」,這樣就會導致資料的不完整。

粘包:傳輸的多條資料粘在一起,比如我發了兩句話「你好」和「我是小李」,我們可能會收到「你好我是小李」,也可能收到「你好我是」「你好我」「你好我是小」,後幾種情況是分包粘包同時發生,我們肯定不期望這種現象發生,所以我們就有必要對我們傳送的資料進行編輯。

解決方案: 我們每傳送的一條資料就是乙個資料報,我們是通過使用位元組流來傳輸資料的,所以當我們每傳送乙個資料報,就順便附帶上資料報的長度,這樣就形成了「資料報頭」+「包體」的結構,包頭用來儲存資料報長度,包體用來儲存具體的資料,每當我們接受資料時,首先讀取資料報頭,得到資料長度,再和已經傳過來的長度對比,如果長度足夠,說明至少傳過來乙個完整的包,我們就可以根據長度來取包體,如果不夠,我們先不讀取,直到長度滿足時,我們再把這條資料進行讀取。

這樣就能方便的解決分包粘包的問題,但是我們之前接受到的不完整的資訊放到哪呢?我們就需要乙個快取區,如果資料不完整,我們先放在快取,當資料完整時,再取出讀取。這裡有一種建立快取區的方案,通過記憶體流來讀取。

快取區:我們使用記憶體流memorystream來進行讀寫操作,如果我們收到資料,我們就將資料寫入記憶體流,當記憶體流的長度滿足包頭長度時,就將訊息取出讀取,流的操作比較基本,可以查閱資料了解,這裡只提供一條思路。

既然說到了資料報,那我們接著把資料報說完,資料報頭+包體是我們最基本的結構,但我們的資料不可能就這樣來傳輸,因為一旦有人截獲我們的資料,就能輕易的修改我們的資料值,對遊戲造成影響,所以我們必須要對資料報進行加密。

一般情況下,我們會使用crc進行冗餘驗證,看資料報是否傳輸完整,然後自定義自己的加密方式,將資料報加密以後再發出,有的專案還會對資料報進行壓縮,所以我們這裡給出一種通用的結構:資料頭(長度)+冗餘驗證(crc)+是否壓縮+包體(加密後)。

這樣,我們就可以對包進行加密操作以及完整性驗證,保證我們的資料正確性、保密性和完整性。

接下來就是傳輸協議,我們傳輸的資料都是位元組流,那麼我們收到這些位元組流後如何處理呢?這就需要我們對資料報進行進一步編輯,給資料報乙個協議id,即把包體分為兩部分:包體=協議id+內容;當我們收到資料後,首先進行拆包,獲取到協議id後,就進行對應id的事件派發,這裡我們會用到觀察者模式,下節會說到;我們派發事件後,對應的功能就會被執行,並且返回新的資料傳送到另一端。那麼我們通常都會有那些協議呢?

通常我們在socket中,最不能缺少的協議就是心跳,心跳是檢驗客戶端和伺服器是否連線、是否斷線的非常有效的方法。伺服器會每隔一段時間給客戶端傳送乙個心跳包,

如果在一定時間內沒有收到客戶端的回應,即認為客戶端已經掉線;同樣,如果客戶端在一定時間內沒 有收到伺服器的心跳包,則認為連線不可用。

除了心跳包,我們還會定義一些通用的包,如郵件、聊天等,具體要根據我們的需要來制定,比如我們要做乙個mmorpg遊戲,那麼我們就有必要定義關於玩家位置、行為等相關的協議。

具體協議的實現,我們通常會用到類來接收包的資料,通常會有乙個基類包,所有的協議類都繼承這個基類,但如果資料的複雜度不是很大的話,我們可以使用結構體,將原來的基類改為使用介面,這也是一種不錯的優化網路模組的方式。

總之,我們在遊戲開發中通常使用弱聯網進行登入驗證,強聯網用於遊戲內部,所以socket的使用在遊戲開發中所佔比例以及重要程度不言而喻,這也是我們不斷研究、不斷探索、不斷優化的原因。下一節我們就具體講一講觀察者模式以及在網路模組中的應用。

socket網路通訊《二》

socket網路通訊 本文注意基於socket來分析tcp連線建立過程。先回顧一下tcp連線建立過程 主機a執行的是tcp客戶端程式,主機b執行的是tcp伺服器程式,最初兩端tcp程序處於closed態,a主動開啟連線,對應客戶端connect函式發起連線,b被動接受連線,對應於伺服器listen函...

python實現網路通訊

不管是哪種方式實現網路連線,都需要使用socket,在使用前都需要建立socket物件。1 建立socket物件 a socket socket.socket socket.af inet,socket.socket stream 使用ipv4的第乙個引數都選擇socket.af ine 第二引數是...

Scala實現網路通訊

用scala實現一台機器到另一台機器的分布式計算功能 配置檔案 summer.properties server.port 9999 server.host localhost 讀取配置檔案工具類 object propertiesutil class else if s serversocket ...