java 網絡通訊傳輸層協議——UDP和TCP

 

本文原文由做者「zskingking」發表於:jianshu.com/p/271b1c57bb0b,本次收錄有改動。php


一、點評


互聯網發展至今已經高度發達,而對於互聯網應用(尤爲即時通信網專一的即時通信技術這一塊)的開發者來講,網絡編程是基礎中的基礎,只有更好地理解相關基礎知識,對於應用層的開發才能作到遊刃有餘。

對於Android程序員來講,若是您以爲本文內容稍顯枯燥,能夠看看即時通信網以前整理過的一篇相似文章《邁向高階:優秀Android程序員必知必會的網絡基礎》,該文內容更偏向於知識點的歸納。

若是您但願更系統地學習網絡編程方面的知識,能夠讀一讀如下專爲初學者整理的系列文章或資料:


二、前言


相信計算機專業的朋友在大學都學過《計算機網絡》這門課程,但據我我的瞭解計算機專業普通大學生對計算機網絡的瞭解淺之又淺,不少人說這門學科沒用,開發的時候也用不着,其實這樣想是不對的。

說一下我我的的體會,以前總是聽別人說OkHttp怎麼這麼好用,但用完以後感受和其餘框架沒多大區別啊,因而就想着去專研鑽研,當時差很少花了一個星期左右把OkHttp源碼看了一遍,代碼時看懂了,可是有些地方不知道爲何這樣作,因此我就下決心把計算機網絡從新學一遍,學完各類網絡協議後再看OkHttp源碼忽然有種面目一新的感受。

在本篇文章裏,會爲你們講述做爲Android程序員的我,對於網絡通訊傳輸層協議UDP、TCP的理解,但願能給你帶來啓發。

參考書籍:《計算機網絡-謝希仁版
參考教程:《韓立剛視頻教程

三、關於做者


網名:zskingking,博客地址:https://www.jianshu.com/u/274367e58472

四、UDP協議


4.1概述


UDP的全稱是User Date Protocal,翻譯成中文是用戶數據包協議,它是一種不可靠的傳輸協議,通常狀況下一個數據包(大概64K)能完成的數據通信使用UDP協議,好比請求DNS解析IP地址使用的就是UDP協議,由於解析IP一個數據包徹底足夠。還有就是文字聊天通常用的也是UDP,一般一段文字消息一個數據包就足夠了,若是發送失敗就再次發送,反正就一個數據包。還有一種傳遞大量數據包使用UDP協議的場景,就是廣播,相似對講機之類的,接收方並不必定能接收到全部的數據包。因此說UDP是一種不可靠的傳輸協議。

UDP的主要特色:

  • 1)UDP是無鏈接的,即發送數據以前是不須要創建鏈接的;
  • 2)UDP使用盡最大努力交付,不保證可靠交付,同時不使用阻塞控制;
  • 3)UDP是面向報文的,UDP沒有擁塞控制,很適合多媒體通訊的要求;
  • 4)UDP支持一對1、一對多、多對1、多對多的交互通訊;
  • 5)UDP的首部開銷小,只須要8個字節。

4.2UDP首部


首先咱們先用一張圖來表示UDP的首部,UDP首部以下圖:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_1.jpg 

UDP首部總共是8個字節,其中源端口、目的端口、長度、檢驗和各佔2字節。有的同窗可能要問了,你怎麼沒把僞首部加進去呢?這個我來說一下,僞首部顧名思義,就是假的首部,它是不會跟隨UDP數據報進行傳輸的,它存在的意義就是爲了計算UDP首部中的檢驗和。

UDP首部存儲的信息:

  • 1)源端口:即發送方的端口號,須要接收方迴應時選用,不須要全爲0;
  • 2)目的端口:接收方端口號;
  • 3)長度:UDP數據報長度,最小爲0(只存在首部);
  • 4)檢驗和: 檢驗UDP數據報在傳輸中是否出錯,是就丟棄。

UDP首部組裝完畢後會將完整的數據報發送到網絡層,跟IP數據報首部組成IP數據報再向上發送。

五、TCP協議


5.1概述


TCP全稱爲Transmission Control Protocol(傳輸控制協議),是一種可靠的面向鏈接傳輸協議,同時它也是一種client-server模式的協議,由於是可靠的傳輸協議,因此它比UDP要複雜的多。

首先說一下TCP具備的一些特性:

  • 1)TCP是面向鏈接的傳輸協議;
  • 2)每一條TCP有且只有兩個端點,爲一對一關係;
  • 3)TCP提供可靠交互的服務;
  • 4)TCP提供全雙工通訊,全雙工爲便可傳輸又可接收;
  • 5)TCP是面向字節流的。

TCP的應用場景:

若是兩個臺主機想要在網絡上傳遞一部1G大小的電影,須要經過什麼協議進行傳輸呢?UDP爲不可靠傳輸協議,傳遞過程當中可能會出現丟包,因此UDP不行,而傳輸層就兩個協議,一個是UDP一個是TCP,UDP傳輸效率高但不可靠,TCP傳輸效率低但它是可靠的,因此想要將傳遞的文件完整的到達目的地能夠經過TCP協議進行傳輸。html


5.2TCP鏈接創建與斷開


在5.1中介紹TCP特性的時候提到,TCP是面向鏈接的,即TCP在傳輸數據前要創建鏈接,數據傳輸完畢後要斷開鏈接。TCP鏈接必需要由客戶端發起。

【5.2.1】TCP創建鏈接過程:

<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_2.jpg 

如上圖2所示:客戶端向服務端發起創建鏈接的請求,服務端接收到請求後告訴客戶端:「我準備好了」,客戶端接收到服務端的響應再給服務端一個確認,此過程總共分爲三步被你們親切的成爲:「三次握手」,三次握手後一個可靠的鏈接就創建了,隨後就能夠進行數據的傳輸了,圖中SYN、ACK這些字段我會在TCP首部中詳細介紹,此處你們能夠忽略。

疑點: TCP創建鏈接爲何是三次握手?兩次握手不是已經能夠創建一個鏈接了嗎?網絡上不少文章對此處的描述大可能是輕描淡寫,還有的說是必需要三次握手才能創建一個可靠鏈接,其實這樣說是不對的,當時我也由於這些不負責任的回答費解了好久。通常狀況下,兩次握手是能夠創建一個TCP鏈接的,在《計算機網絡-謝希仁》中大概是這樣解釋的:「第三次握手是爲了不服務端形成資源的浪費」,爲何這樣說呢?我來給你們舉一個例子:

假如TCP是兩次握手:主機A向主機B發送了一個創建鏈接的請求x,但這個請求在半路里給堵了,主機A沒有獲得主機B的響應因而又發了一個創建鏈接的請求y,主機B收到了請求y,因而給主機A發送了一個確認,此時鏈接創建,數據傳輸完畢後斷開了鏈接,但在斷開鏈接後堵在半路的請求x到達了主機B,此時主機B認爲主機A又給本身發送了一個創建鏈接的請求,因而給主機A發送了一個確認,此時主機B認爲鏈接已經創建,處於等待狀態從而致使主機B資源的浪費。但若是是三次握手就能夠避免這種狀況的出現,因此這纔是TCP第三次握手的緣由。

【5.2.2】TCP斷開鏈接過程:

<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_3.jpg 

如上圖所示:主機A至主機B數據傳輸結束後主機A會向主機B發送一個斷開鏈接請求,主機B收到後給主機A一個確認,A就不可向B傳輸數據了,但此時主機B仍然能夠往主機A傳輸數據,等B->A數據傳輸結束後主機B向主機A發送一個斷開鏈接請求。主機A收到後給予一個確認,這樣就成功斷開一個TCP鏈接,過程分四步,也被你們親切的稱爲:「四次揮手」。

疑點:斷開鏈接爲何是四次揮手?兩次不就能夠了嗎?下面我來用一個形象的例子來個你們解除疑點

TCP是全雙工的:即client和server均可以進行傳輸和接收,假設主機A和主機B創建了一個TCP鏈接,主機A能夠往主機B發送數據同時主機B也能夠往主機A發送數據,如今我將主機A->主機B描述成一根水管x,水管x只能由A流到B,主機B->主機A爲水管y,水管y只能由B流到A,如今水管x已經完成的它的輸送水源工做,此時就能夠將水管x切除,對應圖中前兩次揮手,但此時水管y還在工做,必需要等水管y工做完成後纔可以將其切除,切除水管y對應圖中後兩次揮手。

5.3TCP首部


首先用一張圖來表示TCP首部的構造,TCP首部以下圖所示:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_4.jpg 

TCP首部的各項內容解釋:

  • 1)源端口:發送方端口號;
  • 2)目的端口:接收方端口;
  • 3)序號:數據包的序號,以數據包第一個字節進行表示;
  • 4)確認號:確認收到數據包的序號,一樣以字節進行標識;
  • 5)數據偏移:TCP首部長度,以字節爲單位;
  • 6)保留:保留位,總共6爲,必須都爲0;
  • 7)URG:緊急處理,可提高數據包發送的優先級;
  • 8)ACK:表明確認號是否有效;
  • 9)RST:將創建的鏈接重置;
  • 10)PSH:接收方應儘快將這個報文交給應用層;
  • 11)SYN:同步序號用來發起一個鏈接;
  • 12)FIN:終止一個鏈接。

本小節只是讓你們對TCP首部有一個概念性的認識,因此你可能會對首部中某些字段不太理解,不要緊,在下面的文章中我還會提到這些字段。

5.4TCP進行可靠傳輸


咱們知道網絡傳輸是不可靠的,可能存在丟包的現象,TCP是可靠的傳輸協議,那麼它是怎麼作到可靠傳輸呢?我來用幾張圖爲你們分析TCP師怎樣進行可靠傳輸的。

以下圖所示:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_5.jpg 
  • 狀況a:A發送給B數據包M1,B收到以後進行確認,這樣M1包就發送成功了,以此類推,這是無差錯的狀況。
  • 狀況b:A發送數據包M1給B,但中途被丟棄了,若是A遲遲等不到B的響應那麼A就會從新發送M1包給B。

以下圖所示:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_6.jpg 
  • 狀況a:A發送數據包M1給B,B收到後給A發送一個M1的確認包但該確認包在中途被丟棄了,A遲遲未收到B的確認包就認爲M1發送包或者M1確認包早中途丟失了,因而又發送了一個M1包給B,B又收到了一個M1包,此時B就能夠認爲M1確認包可能在中途丟失了,將重複的M1包丟棄後再給A發送一個M1確認包;
  • 狀況b:A發送數據包M1給B,B收到後給A發送一個M1確認包,但該確認包選擇了一個較遠的傳輸路線或者被阻塞了,A遲遲未收到M1確認包就再給B發送一個M1包,B收到重複的M1包後將其丟棄而後再給A發送一個M1確認包,A收到M1確認後就認爲M1發送成功,但此時A收到半路阻塞的那個M1確認包,而A已經確認了M1包發送成功,因此再次受到M1確認包後什麼也不作。

client-server能夠經過傳遞確認的方式來實現可靠傳輸,但這種傳輸方式有一個缺點,效率過低,由於在未收到確認包前是不能夠發送下一個包的,那麼咱們能不能突破這一限制來提高傳輸效率呢?咱們能夠經過提高信道的利用率來提高傳輸效率,以下圖所示:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_7.jpg 

從上圖咱們能夠看到,A在未收到B確認前發送了10個數據包,在這我就將10個數據包形象的編號爲1-10,A一次發送10個數據包,當B收到數據包1的時候給A一個確認,A收到確認後再發送數據包11,當B收到數據包2的時候給A一個確認,A收到確認後再發送數據包12,以此類推。

上圖中A最多一次能夠連續發送10個數據包,而這個10咱們可不能夠理解爲一個窗口呢?打個比方,如今有一個窗口共有10個格子,每一個格子放一個數據包,發送的數據包放在格子裏面,當收到第一個數據包的確認包後將該數據包從窗口中移出,而後將須要發送的下一個數據包放入窗口。

咱們這裏提到的窗口就是TCP首部裏面說的那個窗口,下面我結合圖給你們分析一遍:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_8.jpg 

上圖中,如今咱們有12個數據包須要發送,窗口大小爲5,因此最多一次可連續發送5個數據包,假設如今窗口中的五個數據包都已經發送完畢,此時收到了數據包1的確認包,那麼綠色窗口就能夠往右邊移一個格子,以下圖:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_9.jpg 

上圖中,數據包6被滑進格子裏,此時數據包6就能夠被髮送,同時數據包1也能夠從緩存中清除,經過這種方式能夠提高信道的利用率從而提高傳輸效率,但這種傳輸方式也有一個缺點,就是接收方每收到一個數據包都要進行一次確認,這是徹底不必的,咱們可不能夠這樣作:每收到5個數據包進行一個確認,以下圖:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_10.jpg 

A一次給B發送了5個數據包,B確認5個數據包都收到了,給A回覆一個6,表明B已經收到了前5個數據包讓A下次從第6個數據包開始發送,經過累積響應這種方式又進一步提高了傳輸效率,但這是理想狀況下,若是說A發送完5個數據包,B只收到了一、二、四、5,數據包3丟了,怎麼辦?是直接給A回覆一個3嗎?是的話四、5都要進行重傳,這樣就得不償失了,而編寫TCP協議那位老哥也想到了這種狀況,因此就指定了相應的策略,接着剛剛說,若是B肯定數據包3丟了或者被阻塞了,那麼它會馬上連續發送3個3,A收到連續的3個3後就認爲數據包3丟了,而後就會只補傳數據包3。

注意點:

上面的內容中我爲了方便講解都是把數據包編成編號進行描述,其實真正的數據包編號不是這樣的,TCP協議是面向字節流的,因此說序號和確認號應以字節爲標準,好比:A如今向B發送了5和數據包共100個字節,B收到這5個數據包後會給A回覆一個101,此時A就會從第101個字節開始進行發送,以此類推。同時經過這種機制也能夠實現斷點的下載。程序員



5.5流量控制


流量控制:用來協調server和client兩端因處理數據速度不一樣所帶來的問題。

舉個例子:

假如主機A要向主機B發送數據,若是主機A發送數據的速度比主機B處理數據的速度要快,那麼極可能致使主機B崩潰,經過TCP流量控制技術能夠調整主機A發送數據的速度從而解決上面的問題。編程


TCP是怎樣實現流量控制的的?首先說明一點,前面咱們描述TCP傳輸數據的時候提到了滑動窗口這個概念,其實不光發送方存在滑動窗口,一樣接收方也存在滑動窗口,接收方收到數據包後會將數據包放入滑動窗口,對數據包操做完畢後將該數據包從滑動窗口中移出,當滑動窗口被填滿時不能夠再接收數據,TCP中發送發窗口和接收方窗口大小是相同的。

因此,經過滑動窗口機制能夠實現流量控制,以下圖所示:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_11.jpg 

上圖中,B爲發送方A爲接收方,當B與A創建鏈接的時候首先會明確本身滑動窗口的大小,假如是10,B就會將rwnd(滑動窗口)設置爲10,A收到後也會將滑動窗口設置爲10,鏈接創建成功會A開始向B發送數據,咱們知道滑動窗口越大發送的速度越快,假如rwnd=10時B處理數據包的速度小於接收數據包的速度那麼滑動窗口會逐漸被填滿,這樣會致使主機B中未處理的數據包愈來愈多最終可能會崩潰,爲了不這種狀況的出現,B能夠在滑動窗口被填滿了以後給A發送一個rwnd=6,A接收到rwnd=6後會將滑動窗口調整爲6進而下降發送數據的速度,一樣B若是以爲A的發送速度過慢也能夠經過設置rwnd的值來調整A的發送速度,B動態的設置A的滑動窗口就稱做爲TCP流量控制技術。

5.6擁塞避免


什麼是擁塞呢?顧名思義就是賭了,數據被堵在半路了,那什麼狀況下會出現數據被堵在半路呢?

舉個例子:

假如現有兩臺主機分別是發送方主機A和接收方主機B,主機B的帶寬爲50M/S,也就是說主機B每秒最多能接收50M的數據,若是主機A的發送速度遠低於50M/S,這種狀況應該是不會出現擁塞現象的,可是若是主機A的發送速度遠大於50M/S,主機B的路由器接手不了這麼多數據只能進行丟棄,路由器也是有CPU、內存和本身的操做系統,當主句A發送速度越快主機B的路由器CPU和內存就要分配更多的資源去處理丟棄數據包,這樣就會致使接收數據包的速度愈來愈低,極端的狀況下可能會出現主機B接收不到數據包的現象,也就是死鎖現象。緩存


若是在進行TCP數據傳輸的時候不進行流量控制很容易出現死鎖現象,由於網絡是你們共用的,因此避免網絡擁塞現象的出現須要全部計算機遵照一種特定的規則,那這種規則是怎樣控制網絡避免擁塞的呢?

先來看下面這張圖:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_12.jpg 

若是不進行擁塞控制就是咱們上面所說的,最終可能會出現上圖中綠線的狀況,如今咱們要經過擁塞控制使網絡數據傳輸按照上圖中藍線進行。

下面咱們來講一下如何進行擁塞控制:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_13.jpg 

首先將滑動窗口設置爲1,而後再傳輸過程當中逐漸以指數倍增長,當滑動窗口到達ssthresh的時候再以加法進行增長,若是出現了擁塞現象就迅速再將滑動窗口設置爲1,ssthresh減少依次循環,這種方式也稱爲慢開始方式。但這種方式已經被廢棄,由於每次出現擁塞的時候都會將滑動窗口設置爲0再進行慢開始階段,這樣實際上是徹底不必的。

咱們再來看升級版,以下圖:
<ignore_js_op>Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP_14.jpg 

在出現擁塞的時候並不會將滑動窗口設置爲1從新進行慢開始,而是將滑動窗口設置爲出現擁塞時窗口的一半,而後再以加法進行增長,此過程也可稱爲是快恢復,這樣就能夠避免網絡擁塞的出現。

附錄:更多網絡編程文章


技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)
通俗易懂-深刻理解TCP協議(上):理論基礎
通俗易懂-深刻理解TCP協議(下):RTT、滑動窗口、擁塞處理
理論經典:TCP協議的3次握手與4次揮手過程詳解
理論聯繫實際:Wireshark抓包分析TCP 3次握手、4次揮手過程
計算機網絡通信協議關係圖(中文珍藏版)
UDP中一個包的大小最大能多大?
P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介
P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解
P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解
通俗易懂:快速理解P2P技術中的NAT穿透原理
高性能網絡編程(一):單臺服務器併發TCP鏈接數到底能夠有多少
高性能網絡編程(二):上一個10年,著名的C10K併發鏈接問題
高性能網絡編程(三):下一個10年,是時候考慮C10M併發問題了
高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索
高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型
高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型
鮮爲人知的網絡編程(一):淺析TCP協議中的疑難雜症(上篇)
鮮爲人知的網絡編程(二):淺析TCP協議中的疑難雜症(下篇)
鮮爲人知的網絡編程(三):關閉TCP鏈接時爲何會TIME_WAIT、CLOSE_WAIT
鮮爲人知的網絡編程(四):深刻研究分析TCP的異常關閉
鮮爲人知的網絡編程(五):UDP的鏈接性和負載均衡
鮮爲人知的網絡編程(六):深刻地理解UDP協議並用好它
鮮爲人知的網絡編程(七):如何讓不可靠的UDP變的可靠?
技術掃盲:新一代基於UDP的低延時網絡傳輸層協議——QUIC詳解
讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享
現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障
聊聊iOS中網絡編程長鏈接的那些事
移動端IM開發者必讀(一):通俗易懂,理解移動網絡的「弱」和「慢」
移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結
IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)
IPv6技術詳解:基本概念、應用現狀、技術實踐(下篇)
從HTTP/0.9到HTTP/2:一文讀懂HTTP協議的歷史演變和設計思路
以網遊服務端的網絡接入層設計爲例,理解實時通訊的技術挑戰
邁向高階:優秀Android程序員必知必會的網絡基礎
全面瞭解移動端DNS域名劫持等雜症:技術原理、問題根源、解決方案等
美圖App的移動端DNS優化實踐:HTTPS請求耗時減少近半
Android程序員必知必會的網絡通訊傳輸層協議——UDP和TCP
>> 更多同類文章 ……
相關文章
相關標籤/搜索