網遊中的網絡編程系列1:UDP vs. TCP

原文:UDP vs. TCP,做者是Glenn Fiedler,專一於遊戲網絡編程相關工做多年。html

目錄

  1. 網遊中的網絡編程系列1:UDP vs. TCP
  2. 網遊中的網絡編程2:發送和接收數據包
  3. 網遊中的網絡編程3:在UDP上創建虛擬鏈接
  4. TODO

說在最前面的話

翻譯這篇文章的初衷:我在工做中根本接觸不到網絡遊戲編程,可是我不想把本身定義爲‘網站開發工程師’,正像我師父告訴個人:「別說開發網站,太low!要說開發web應用」。那麼,網絡遊戲開發web方面的知識真的應該瞭解下。鍛鍊本身英文的同時,能夠分享些東西給「小夥們」,哇咔咔~。web

這是一個系列的文章,前面的東西必定要弄懂,後面就會輕鬆的搞明白(我後面的還沒看,可是要先給本身打打氣😋)。編程

注:括號中的內容都是我加上去的,爲了有助於理解。瀏覽器

介紹

你必定據說過sokcet(初探socket),它分爲兩種經常使用類型:TCP和UDP。當要寫一個網絡遊戲,咱們首先要選擇使用哪一種類型的socket。是用TCP、UDP仍是二者都用?服務器

選擇哪一種類型,徹底取決於你要寫的遊戲的類型。後面的文章,我都將假設你要寫一個‘動做’網遊。就像:光環系列,戰地1942,雷神之錘,這些遊戲。網絡

咱們將很是仔細的分析這兩種socket類型的優劣,而且深刻到底層,弄清楚互聯網是如何工做的什麼。當咱們弄清楚這些信息後,就很容易作出正確的選擇了。socket

TCP/IP

TCP表明「傳輸控制協議」,IP表明:「互聯網協議」,你在互聯網上作任何事情,都是創建在這二者的基礎上,好比:瀏覽網頁、收發郵件等等。tcp

TCP

若是你曾經用過TCP socket,你確定知道它是可靠鏈接的協議,面向鏈接的傳輸協議。簡單的說:兩臺機器先創建起鏈接,而後兩臺機器相互發送數據,就像你在一臺計算機上寫文件,在另一個臺讀文件同樣。(我是這麼理解的:TCP socket就像創建起鏈接的計算機,之間共享的一個'文件'對象,二者經過讀寫這個'文件'實現數據的傳輸)性能

這個鏈接是可靠的、有序的,表明着:發送的全部的數據,保證到達傳輸的另外一端的時候。另外一端獲得的數據,和發送數據一摸同樣(可靠,有序。例如:A發送數據‘abc’,經過TCP socket傳輸數據到B,B獲得數據必定是:‘abc’。而不是‘bca’或者‘xueweihan’之類的鬼!😋)。傳輸的數據是‘數據流’的形式(數據流:用於操做數據集合的最小的有序單位,與操做本地文件中的stream同樣。因此TCP socket和文件對象很像),也就是說:TCP把你的數據拆分後,包裝成數據包,而後經過網絡發送出去。學習

注意:就像讀寫文件那樣,這樣比較好理解。

IP

「IP」協議是在TCP協議的下面(這個牽扯到七層互聯網協議棧,我就簡單的貼個圖不作詳細的介紹)

「IP」協議是沒有鏈接的概念,它作的只是把上一層(傳輸層)的數據包從一個計算傳遞到下一個計算機。你能夠理解成:這個過程就像一堆人手遞手傳遞紙條同樣,傳遞了不少次,最終到達紙條上標記的xxx手裏(紙條上寫着‘xxx親啓,偷看者3cm’😄)。

在傳遞的過程當中,不保證這個紙條(信件)能可以準確的送到收信人的手上。發信人發送信件,可是永遠不知道信件是否能夠準確到達收件人的手上,除非收件人回信告訴他(發信人):「兄弟我收到信了!」(IP層只是用於傳遞信息,並不作信息的校驗等其它操做)

固然,傳遞信息的這個過程仍是仍是很複雜的。由於,不知道具體的傳遞次序,也就是說,由於不知道最優的傳遞路線(可以讓數據包快速的到達目的地的最優路徑)因此,有些時候「IP」協議就傳遞多份同樣的數據,這些數據經過不一樣的路線到達目的地,從而發現最優的傳遞路線。

這就是互聯網設計中的:自動優化和自動修復,解決了鏈接的問題。這真的是一個很酷的設計,若是你想知道更多的底層實現,能夠閱讀關於TCP/IP的書。(推薦上野宣的圖解系列)

UDP

若是咱們想要直接發送和接受數據包,那麼就要使用另外一種socket。

咱們叫它UDP。UDP表明「用戶數據包協議」,它是另一種創建在IP協議之上的協議,就像TCP同樣,可是沒有TCP那麼多功能(例如:創建鏈接,信息的校驗,數據流的拆分合並等)

使用UDP咱們可以向目標IP和端口(例如80),發送數據包。數據包會達到目標計算機或者丟失。

收件人(目標計算機),咱們只須要監聽具體的端口(例如:80),當從任意一臺計算機(注意:UDP是不創建鏈接的)接受到數據包後,咱們會得知發送數據包的計算機地址(IP地址)和端口、數據包的大小、內容。

UDP是不可靠協議。現實使用的過程當中,發送的大多數的數據包都會被接收到,可是一般會丟失1-5%,偶爾,有的時候還可能啥都接收不到(數據包所有丟失一個都沒接收到,傳遞數據的計算機之間的計算機的數量越多,出錯的機率越大)。

UDP協議中的數據包也是沒有順序的。好比:你發送5個包,順序是1,2,3,4,5。可是,即接收到的順序多是3,1,4,2,5。現實使用的過程當中,大多時候,接收到的數據的順序是正確的,可是並非每次都是這樣。

最後,儘管UDP並無比「IP」協議高級多少,並且不可靠。可是你發送的數據,要麼所有到達,要麼所有丟失。好比:你發送一個大小爲256 byte的數據包給另一臺計算機,這臺計算機不會只接收到100 byte的數據包,它只可能接收到256 byte的數據包,或者什麼都沒接收到。這是UDP惟一能夠保證的事情,其它全部的事情都須要你來決定(個人理解,UDP協議只是個簡單的傳輸協議,只保證數據包的完整性,注意是數據包而不是信息。其餘的事情須要本身去作,完善這個協議,達到本身使用的需求。)

TCP vs. UDP

咱們如何選擇是使用TCP socket仍是UDP socket呢?

咱們先看看二者的特徵吧:

TCP:

  • 面向鏈接

  • 可靠、有序

  • 自動把數據拆分紅數據包

  • 確保數據的發送一直在控制中(流量控制)

  • 使用簡單,就像讀寫文件同樣

UDP:

  • 沒有鏈接的概念,你須要本身經過代碼實現(這個我也沒本身實現過,應該還會講)

  • 不可靠,數據包無序,數據包可能無序,重複,或者丟失

  • 你須要手動地把數據拆分紅數據包,而後發送數據包

  • 你須要本身作流量控制

  • 若是數據包太多,你須要設計重發和統計機制

經過上面的描述,不難發現:TCP作了全部咱們想作的事情,並且使用十分簡單。反觀UDP就十分難用了,咱們須要本身編寫設計一切。很顯然,咱們只要用TCP就行了!

不,你想的簡單了(原來,是我太年輕了!)

當你開發一個像上面說過的FPS(動做網遊)的時候使用TCP協議,會是一個錯誤的決定,這個TCP協議就很差用了!爲何這麼說?那麼你就須要知道TCP到底作了什麼,使得一塊兒看起來十分簡單。(讓咱們繼續往下看,這是我最好奇的地方!!!有沒有興奮起來?🤓)

TCP內部的工做原理

TCP和UDP都是創建在「IP」協議上的,可是它倆徹底不一樣。UDP和「IP」協議很像,然而TCP隱藏了數據包的全部的複雜和不可靠的部分,抽象成了相似文件的對象。

那麼TCP是如何作到這一點呢?

首先,TCP是一個數據流的協議,因此你只須要把輸入的內容變成數據流,而後TCP協議就會確保數據會到達發送的目的地。由於「IP」協議是經過數據包傳遞信息,TCP是創建在「IP」協議之上,因此TCP必須把用戶輸入的數據流分紅數據包的形式。TCP協議會對須要發送的數據進行排隊,而後當有足夠的排除數據的時候,就發送數據包到目標計算機。

當在多人在線的網絡遊戲中發送很是小的數據包的時候,這樣作就有一個問題。這個時候會發生什麼?若是數據沒有達到緩衝區設定的數值,數據包是不會發送的。這就會出現個問題:由於客戶端的用戶輸入請求後,須要儘快的從服務器獲得響應,若是像上面TCP 等待緩衝區滿後才發送的話,就會出現延時,那麼客戶端的用戶體驗就會很是差!網絡遊戲幾乎不能出現延時,咱們但願看到的是「實時」和流暢。

TCP有一個選項能夠修復,上面說的那種等待緩衝區滿才發送的狀況,就是TCP_NODELAY。 這個選項使得TCP socket不須要等待緩衝區滿才發送,而是輸入數據後就當即發送。

然而,即便你已經設置了TCP_NODELAY選項,在多人網遊中仍是會有一系列的問題。

這一切的源頭都因爲TCP處理丟包和亂序包的方式。使得你產生有序和可靠的「錯覺」。

TCP如何保證數據的可靠性

本質上TCP作的事情,分解數據流,成爲數據包,使用在不可靠的「IP」協議,發送這些數據包。而後使得數據包到達目標計算機,而後重組成數據流。

可是,如何處理當丟包?如何處理重複的數據包和亂序數據包?

這裏不會介紹TCP處理這些事情的細節,由於這些都是很是複雜的(想弄清楚的同窗能夠看我上面推薦的書單),大致上:TCP發送一個數據包,等待一段時間,直到檢測到數據包丟失了,由於沒有接收到它的ACK(一種傳輸類控制符號,用於確認接收無誤),接下來就從新發送丟失的數據包到目標計算機。重複的數據包將被丟棄在接收端,亂序的數據包將被從新排序。因此保證了數據包的可靠性和有序性。

若是咱們用TCP實現數據的實時傳輸,就會出現一個問題:TCP不管什麼狀況,只要數據包出錯,就必須等待數據包的重發。也就是說,即便最新的數據已經到達,但仍是不能訪問這些數據包,新到的數據會被放在一個隊列中,須要等待丟失的包從新發過來以後,全部數據沒有丟失才能夠訪問。須要等待多長時間才能從新發送數據包?舉個例子:若是的延時是125ms,那麼須要最好的狀況下重發數據包須要250ms,可是若是遇到糟糕的狀況,將會等待500ms以上,好比:網絡堵塞等狀況。那就沒救了。。。

爲何TCP不該該用於對網絡延時要求極高的條件下

若是FPS(第一人稱射擊)這類的網絡遊戲使用TCP就出現問題,可是web瀏覽器、郵箱、大多數應用就沒問題,由於多人網絡遊戲有實時性的要求。好比:玩家輸入角色的位置,重要的不是前一秒發生了什麼,而是最新的狀況!TCP並無考慮這類需求,它並非爲這種需求而設計的。

這裏舉一個簡單的多人網遊的例子,好比射擊的遊戲。對網絡的要求很簡單。玩家經過客戶端發送給服務器的每一個場景(用鼠標和鍵盤輸入的行走的位置),服務器處理每一個用戶發送過來的全部場景,處理完再返回給客戶端,客戶端解析響應,渲染最新的場景展現給玩家。

在上面說的哪一個多人遊戲的例子中,若是出現一個數據包丟失,全部事情都須要停下來等待這個數據包重發。客戶端會出現等待接收數據,因此玩家操做的任務就會出現站着不動的狀況(卡!卡!卡!😡💢),不能射擊也不能移動。當重發的數據包到達後,你接收到這個過期的數據包,然而玩家並不關心過時的數據(激戰中,卡了1秒,等能動了,都已經死了💀)

不幸的是,沒有辦法修復TCP的這個問題,這是它本質的東西,沒辦法修復。這就是TCP如何作到讓不可靠,無序的數據包,看起來像有序,可靠的數據流。

我並不須要可靠,有序的數據流,咱們但願的是客戶端和服務端之間的延時越低越好,不須要等待重發丟失的包。

因此,這就是爲何在對數據的實時性要求的下,咱們不用TCP。

那爲何不UDP和TCP一塊兒用呢?

像玩家輸入實時遊戲數據和狀態的變動,只和最新的數據有關(這些數據強調實時性)。可是另外的一些數據,例如,從一臺計算機發送給另一個臺計算機的一些列指令(交易請求,聊天?),可靠、有序的傳輸仍是很是重要的!

那麼,用戶輸入和狀態用UDP,TCP用於可靠、有序的數據傳輸,看起來是個不錯的點子。可是,問題在於TCP和UDP都是創建「IP」協議之上,因此協議之間都是發送數據包,從而相互通訊。協議之間的互相影響是至關複雜的,涉及到TCP性能、可靠性和流量控制。簡而言之,TCP會致使UDP丟包,請參考這篇論文

此外,UDP和TCP混合使用是很是複雜的,並且實現起來是很是痛苦的。(這段我就不翻譯了,總而言之:不要混用UDP和TCP,容易失去對傳輸數據的控制)

總結

個人建議並非就必定要使用UDP,可是UDP協議應該用於遊戲。請不要混合使用TCP和UDP,你應該學習TCP中一些地方是如何實現的技巧,而後能夠把這些技巧用在UDP上,從而實現適合你的需求的協議(借鑑TCP中的實現,在UDP上,完善功能,從而達到你的需求)。

這個系列,接下來會講到:如何在UDP上建立一個虛擬的鏈接(由於UDP自己,是沒有鏈接的概念的)、如何使得UDP實現可靠性,流量控制,非阻塞。

參考

相關文章
相關標籤/搜索