UDP和TCP

面試題

TCP和UDP有哪些區別?

UDP協議是面向無連接的,TCP是面向連接的

所謂建立連接,是爲了客戶端和服務端維護連接,而建立一定的數據結構來維護雙方交互的狀態,用這樣的數據結構來保證所謂的面向連接的特性。

面向無連接也就是說不需要在正式傳遞數據之前先連接雙方。
UDP具體的說就是:

  • 在發送端,應用層將數據傳遞給傳輸層的UDP協議,UDP只會給數據增加一個UDP頭部標識,然後就傳遞給網絡層
  • 在接收端,網絡層將數據傳遞給傳輸層,UDP只去除IP報文就傳遞給應用層,不會任何拼接操作

面向連接的TCP,在正式傳遞數據之前需要連接雙方,這裏使用的是三次握手

TCP提供可靠交付,UDP不可靠

通過TCP連接運輸,無差錯,不丟失,不重複,並且按序到達。

我們都知道IP包是沒有任何可靠性保證的,一旦發出去,走丟了,缺失了,都不會處理,UDP繼承了IP包的特性,不保證不丟失,不保證按順序到達。

TCP是面向字節流的,UDP基於數據報的(TCP是一種流模式的協議,UDP是一種數據報模式的協議)

TCP發送的時候是一個流,沒頭沒尾,IP包可不是一個流,而是一個個的IP包,之所以變成了流,這也是TCP自己的狀態維護做的事情,而UDP繼承了IP的特性,基於數據包的,一個一個的發送

“TCP是一種流模式的協議,UDP是一種數據報模式的協議”,這句話相信大家對這句話已經耳熟能詳~但是,“流模式”與“數據包模式”在編程的時候有什麼區別呢?以下是我的理解,僅供參考!

1、TCP
打個比方比喻TCP,你家裏有個蓄水池,你可以裏面倒水,蓄水池上有個龍頭,你可以通過龍頭將水池裏的水放出來,然後用各種各樣的容器裝(杯子、礦泉水瓶、鍋碗瓢盆)接水。
上面的例子中,往水池裏倒幾次水和接幾次水是沒有必然聯繫的,也就是說你可以只倒一次水,然後分10次接完。另外,水池裏的水接多少就會少多少;往裏面倒多少水,就會增加多少水,但是不能超過水池的容量,多出的水會溢出。
結 合TCP的概念,水池就好比接收緩存,倒水就相當於發送數據,接水就相當於讀取數據。好比你通過TCP連接給另一端發送數據,你只調用了一次write, 發送了100個字節,但是對方可以分10次收完,每次10個字節;你也可以調用10次write,每次10個字節,但是對方可以一次就收完。(假設數據都 能到達)但是,你發送的數據量不能大於對方的接收緩存(流量控制),如果你硬是要發送過量數據,則對方的緩存滿了就會把多出的數據丟棄。
2、UDP
UDP 和TCP不同,發送端調用了幾次write,接收端必須用相同次數的read讀完。UPD是基於報文的,在接收的時候,每次最多隻能讀取一個報文,報文和 報文是不會合並的,如果緩衝區小於報文長度,則多出的部分會被丟棄。也就說,如果不指定MSG_PEEK標誌,每次讀取操作將消耗一個報文。
3、爲什麼
其實,這種不同是由TCP和UDP的特性決定的。TCP是面向連接的,也就是說,在連接持續的過程中,socket中收到的數據都是由同一臺主機發出的(劫持什麼的不考慮),因此,知道保證數據是有序的到達就行了,至於每次讀取多少數據自己看着辦。
而 UDP是無連接的協議,也就是說,只要知道接收端的IP和端口,且網絡是可達的,任何主機都可以向接收端發送數據。這時候,如果一次能讀取超過一個報文的 數據,則會亂套。比如,主機A向發送了報文P1,主機B發送了報文P2,如果能夠讀取超過一個報文的數據,那麼就會將P1和P2的數據合併在了一起,這樣 的數據是沒有意義的。
TCP是可以有擁塞控制的,UDP則沒有

TCP意識到包丟失或者是網絡環境不好了,就會根據情況調整自己的行爲,看看是不是發快了,要不要發慢點,UDP就不會,應用讓我發我就發,不管網絡情況,那麼堵的很,我還要發,有點像愣頭青的樣子

UDP 高效

UDP沒有像TCP那樣複雜,頭部開銷很小隻有八個字節,相比TCP的至少二十個字節要少很多,在傳輸數據報文時,是很高效的。

TCP是有狀態的服務,UDP則是無狀態服務

TCP能精確地記着發送了沒有,接受了沒有,發送到了哪?應該接受哪個?錯一點都不行。
UDP 通俗地講就是沒腦子,啥也不管,不記錄。

傳輸方式 每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信.

UDP

UDP的三大特點
溝通簡單

不需要大量的數據結構,處理邏輯,包頭字段。前提是它相信網絡是美好的,相信網絡通路默認就是很容易送達的,不容易被丟棄的

輕信他人

他不會建立連接,雖然有端口號,但是監聽在這個地方,誰都可以傳給他數據,他也可以傳給任何人數據,甚至可以同時傳給多個人數據

做事不懂變通

不知道什麼時候堅持,什麼時候退讓,不會根據網絡的情況進行發包的擁塞控制

UDP的三大使用場景
第一 需要資源少,網絡情況比較好的內網,或者對於丟包不敏感的應用

其中DHCP協議(動態主機配置協議)就是基於UDP,操作系統鏡像的下載使用的TFTP(簡單文件傳輸協議,端口是69),也是基於UDP協議的

不需要一對一溝通,建立連接,而是可以廣播的應用。

DHCP就是一種廣播的形式

需要處理速度快,時延低,可以容忍少數丟包,但是要求即便網絡擁塞,也毫不退縮,一往無前。

很多應用都是要求低時延的,它們一般是不想用TCP如此複雜的機制,而是根據自己的場景實現自己的可靠和連接保證。例如,有的應用覺得,有的包丟了就丟了,沒必要重傳,後面的包到了,就先給客戶端展示,不一定非要等到齊,如果網絡不好,那也要儘快傳,速度不能降下來,搶在客戶失去耐心之前到達。

基於UDP的五個實例
網頁或者APP的訪問

原來訪問網頁和手機APP都是基於HTTP協議的,HTTP協議是基於TCP的,建立連接都需要多次交互,對於時延比較大的主流移動互聯網來說,建立一次鏈接需要的時間會比較長,然後既然是移動中,TCP可能還會斷了重連,很耗時的。而且目前的HTTP協議,往往採取多個數據通道共享一個連接,這樣本來爲了加快速度,但是TCP的嚴格順序策略使得哪怕共享通道,前一個不來,後一個即使和前一個沒有關係,它也要等着,時延會加大

而QUIC(Quick UDP Internet Connections 快速UDP互聯網連接),是Google提出的一種基於UDP改進的通信協議,其主要目的就是降低網絡延時,提供更好的用戶互動體驗

QUIC在應用層上,會自己實現快速建立連接,減少重傳時延,自適應擁塞控制。

流媒體協議

以前直播中協議 使用的是RTMP(實時消息傳輸協議),這個協議是基於TCP,TCP都知道,慢的一批,現在直播就是基於UDP,實現自己的視頻協議

實時遊戲
物聯網

物聯網領域終端資源少,很可能只是內存非常小的嵌入式系統,而維護TCP協議代價很大,
物聯網對實時性要求也很高,Google的一家公司Nest建立的Thread Group 推出物聯網通信協議Thread,就是基於UDP的

移動通信領域

在4G網絡裏,移動流量上網的數據面對的協議GTP-U就是基於UDP的,因爲這個協議本身就比較複雜。

TCP

三次握手

A : SYN

客戶端進入SYN-SENT狀態

B : SYN ACK

服務端進入SYN-RCVD狀態

A : ACK

雙方進入ESTABLISHED(已建立)

三次握手除了雙方建立連接外,主要還是爲了溝通一件事請,就是TCP包的序號問題

A要告訴B,我這面發起的包的序號起始是從哪個號開始的,B同樣告訴A,B發起的包的序號是從哪個號開始的,爲什麼不能都從1開始,因爲這樣往往會產生衝突

比如:A連接上B之後,發送了1,2,3三個包,但是發送3的時候,中間丟了,或者是繞路了,於是重新發送,後來A掉線了,重新連接上B後,序號又從1開始發送,然後發送2,但是壓根沒有向發送序號爲3的包,但是上次繞路的那個3有回來了,發給了B,B自然認爲就是下一個包,這時候就發生了錯誤。

因而,每個連接都要有不同的序號,這個序號的起始序號是隨時間變化的,可以看成是一個32位的計數器,沒4ms加一,如果計算一下,繞路的 包就會很早就死了。因爲IP包頭裏面有個TTL,也即生存時間

四次揮手

A : FIN

客戶端A進入FIN-WAIT-1狀態

B : ACK

服務端B進入CLOSED-WAIT狀態

B : FIN,ACK

服務端B進入LAST-ACK狀態

A : ACK

客戶端A進入TIME-WAIT(2MSL)

最後2MSL過後,雙方都進入了CLOSED狀態

如何實現一個靠譜的協議?

爲了保證順序性,每一個包都應該有個ID,在建立連接的時候,會商定起始的ID是什麼,然後按照ID一個個發送,爲了保證不丟包,對於發送的包都要進行應答,但是這個應答也不是一個一個來的,而是會應答某個之前的ID,表示都收到了,這種模式成爲累計確認或者累計應答

詳細內容看專欄

順序與丟包問題

TCP具體是通過怎樣的方式來保證數據的順序化傳輸呢?

主機每次發送數據時,TCP就給每個數據包分配一個序列號並且在一個特定的時間內等待接收主機對分配的這個序列號進行確認,如果發送主機在一個特定時間內沒有收到接收主機的確認,則發送主機會重傳此數據包。接收主機利用序列號對接收的數據進行確認,以便檢測對方發送的數據是否有丟失或者亂序等,接收主機一旦收到已經順序化的數據,它就將這些數據按正確的順序重組成數據流並傳遞到高層進行處理。

具體步驟如下:
(1)爲了保證數據包的可靠傳遞,發送方必須把已發送的數據包保留在緩衝區;

(2)併爲每個已發送的數據包啓動一個超時定時器;

(3)如在定時器超時之前收到了對方發來的應答信息(可能是對本包的應答,也可以是對本包後續包的應答),則釋放該數據包占用的緩衝區;

(4)否則,重傳該數據包,直到收到應答或重傳次數超過規定的最大次數爲止。

(5)接收方收到數據包後,先進行CRC校驗,如果正確則把數據交給上層協議,然後給發送方發送一個累計應答包,表明該數據已收到,如果接收方正好也有數據要發給發送方,應答包也可方在數據包中捎帶過去

流量控制問題

所謂的流量控制就是讓發送方的發送速率不要太快,讓接收方來得及接受。利用滑動窗口機制可以很方便的在TCP連接上實現對發送方的流量控制。

TCP頭裏有一個字段叫Window(或Advertised Window),用於接收方通知發送方自己還有多少緩衝區可以接收數據。發送方根據接收方的處理能力來發送數據,不會導致接收方處理不過來,是謂流量控制。暫且把Advertised Window當做滑動窗口,更容易理解滑動窗口如何完成流量控制

由滑動窗口協議(連續ARQ協議)實現。滑動窗口協議既保證了分組無差錯、有序接收,也實現了流量控制。主要的方式就是接收方返回的 ACK 中會包含自己的接收窗口的大小,並且利用大小來控制發送方的數據發送。

詳情請看掘金專欄 TCP
滑動窗口
發送端窗口包含已經發送但未收到應答的數據和可以發送但是未發送的數據

發送端的窗口是由接受端窗口剩餘大小決定的,接收方會把當前接收窗口的剩餘大小寫入應答報文,發送端收到應答報文後,會隨之窗口進行滑動

Zerp窗口

在發送報文的過程,可能會出現對端出現零窗口的情況,在該情況下,發送端會停止發送數據,並啓動persistent timer,該定時器會定時發送請求給對端,讓對端告知窗口大小,在重試次數超過一定次數後,可能會中斷TCP連接

擁塞控制

擁塞處理和流量控制不同,後者是作用於接收方,它是控制發送者的發送速度從而使接收者來得及接收,防止分組丟失的。

擁塞處理作用於網絡,防止過多的數據擁塞網絡,避免出現網絡負載過大的情況

擁塞處理包括了四個算法:慢開始,擁塞避免,快速重傳,快速恢復

慢開始算法

就是在傳輸開始時將發送窗口慢慢指數級擴大,從而避免一開始就傳輸大數量導致網絡阻塞。相比大家都下載過資源吧,每當我們開始下載的時候都會發現下載的速度是慢慢提升的,而不是一蹴而就直接拉滿寬帶。

慢開始算法步驟具體如下:
  • 連接初始設置擁塞窗口爲1MSS(一個分段的最大數據量)
  • 每過一個RTT就將窗口大小乘二
  • 指數級增長肯定不能沒有限制的,所以有一個閾值限制,當窗口大小大於閾值時就會啓動擁塞避免算法
擁塞避免算法

擁塞避免算法相比簡單點,每過一個RTT窗口大小隻加一,這樣能避免指數級增長導致網絡阻塞,慢慢將大小調整到最佳值

在傳輸過程中可能定時器超時的情況,這時候TCP就會認爲網絡擁塞了,會馬上進行以下步驟:

  • 將閾值設爲當前擁塞窗口的一半
  • 將擁塞窗口設爲1MSS
  • 啓動擁塞避免算法
快速重傳

快速重傳一般和快恢復一起出現,一旦接收端收到的報文出現失序的情況,接收端只會回覆最後一個順序正常的報文序號,如果發送端接收到三個重複的ACK,無需等待定時器超時而是直接啓動快速重傳算法,具體的算法分爲兩種:

TCP Taho 實現如下:
  • 將閾值設置爲當前擁塞窗口的一半
  • 將擁塞窗口設爲1MSS
  • 重新開始滿開始算法
TCP Reno實現如下
  • 擁塞窗口減半
  • 將閾值設爲當前擁塞窗口
  • 進入快恢復階段(重發對端需要的包,一旦接收一個新的ACK答覆就退出該階段),這種方式在丟失多個包的情況下就不怎麼好
  • 使用擁塞避免算法
TCP的擁塞控制主要是來避免兩種現象,包丟失和超時重傳

一旦出現了這些現象就說明,發送的速度太快了,要慢一點。

常見面試題

爲什麼客戶端在TIME_WAIT狀態必須當代2MSL的時間

1 爲了保證客戶端發送的最後一個ACK報文段能夠到達服務器,這個ACK報文段有可能丟失,因爲使處於LAST-ACK狀態的服務器收不到對方發送的FIN+ACK報文段,服務器會超時重傳FIN+ACK這個報文段,而客戶端就能在2MSL時間內收到這個報文,然後重傳,重新啓動2MSL計時器,最後,客戶端和服務器都正常進入到CLOSED狀態。
如果客戶端沒有等待一段時間,就無法收到服務器發送的重傳報文,因而不會再次發送去確認報文,那麼服務器就無法按照正常步驟進入CLOSED狀

2 防止已失效連接請求報文段,出現在本連接中,客戶端發送完最後一個ACK報文後,再經時間2MSL,就可以是本地連接持續的時間內所產生的所有報文都從網絡中消失,這樣就不會在下一個新連接中出現失效的連接請求報文

爲什麼連接的時候是三次握手,關閉的時候卻是四次握手?

因爲當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,“你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

爲什麼不能用兩次握手進行連接?

3次握手完成兩個重要的功能,既要雙方做好發送數據的準備工作(雙方都知道彼此已準備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被髮送和確認。

現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作爲例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S認爲連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已準備好,不知道S建立什麼樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認爲連接還未建立成功,將忽略S發來的任何數據分 組,只等待連接確認應答分組。而S在發出的分組超時後,重複發送同樣的分組。這樣就形成了死鎖。

如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?

TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75分鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。

超時重傳和快速重傳

超時重傳:當超時時間到達時,發送方還未收到對端的ACK確認,就重傳該數據報

快速重傳:當後面的序號先到達,如接收方接收到了1,3,4而2沒有收到,就會立即向發送方重複發送三次ACK=2的確認請求重傳,如果發送方連續收到了3個相同序號的ACK,就重傳該數據包,而不用等到超時。

TCP首部字段,有哪些字段
  • 源端口號 16位
  • 目的端口號 16位
  • 序號 32位
  • 確認序號 32位
  • 首部長度 4位
  • 保留 6位
  • 窗口大小16位
  • 校驗位 16位
  • 緊急指針 16位
  • 選項(長度可變)
  • 標誌位一共6位
  • FIN結束
  • SYN請求鏈接
  • RET重置
  • ACK確認
  • PSH (推送)
  • URG(緊急 和緊急指針一起)
SYN洪泛攻擊

我們在TCP三次握手的討論中已經看到,服務器爲了響應一個收到的SYN,分配並初始化連接變量和緩存。然後服務器發送一個SYNACK進行響應,並等待來自客戶端的ACK報文段,如果某客戶不發送ACK來完成該三次握手的第三部,最終(通常在一分鐘之後)服務器終止該半開連接並回收資源

這種TCP連接管理協議爲經典的Dos攻擊中的SYN洪泛攻擊提供了環境。在這種攻擊中,攻擊者發送大量的TCP SYN報文段,而不完成第三次握手的步驟,隨着這種SYN報文紛至沓來,服務器不斷爲這些半開連接分配資源(但從未用),導致服務器連接資源被消耗殆盡

防禦措施SYNCookie 被部署在主流的操作系統中 工作方式如下:

當服務器接收到一個SYN報文時,它並不知道該報文段是來自一個合法的用戶,還是一個SYN洪泛攻擊的一部分。因此服務器不會爲該報文生成一個半開連接。相反服務器生成一個初始TCP序號,該序列號是SYN報文段的源和目的IP地址與端口號以及僅有該服務器知道的祕密數的一個複雜函數(散列函數)。這種精心製作的初始序列號被稱爲"“Cookie”,服務器則發送具有這種特殊初始序列號的SYNACK分組。重要的是,服務器並不記憶該Cookie或任何對應於SYN的其他狀態

如果客戶端是合法的,則它返回一個ACK報文段。當服務器收到該ACK,需要驗證該ACK是與前面發送的某些SYN相對應的,如果服務器沒有維護有關SYN報文段的記憶,這是怎麼完成的呢?正如你猜測的那樣,它是藉助於cookie來做到的。前面講過對於一個合法的ACK,在確認字段中的值等於在SYNACK字段(此時爲cookie值)中的值加1,服務器則將使用在SYNACK報文段中的源和目的地IP地址與端口號以及祕密數運行相同的散列函數。如果該函數的結果加1與在客戶的SYNACK中的確認(cookie)值相同的話,服務器認爲該ACK對應於較早的SYN報文段,因此它是合法的服務器則生成一個具有套接字的全開的連接

在另一方面,如果客戶沒有返回一個ACK報文段,則初始的SYN並沒有對服務器產生危害,因爲服務器沒有爲它分配任何資源

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章