TCP乾貨

目錄

 

一、起源

二、TCP 協議

怎麼理解全雙工?

TCP 的數據包如何組織?

三、TCP 工作流程

 四、 三次握手

五、 四次揮手


一、起源

TCP 是傳輸層的協議,全稱是叫做 Transmission Control Protocol,這個協議在 IETF RFC 793 進行了定義。 在互聯網產生之前,我們的電腦都是相互獨立的,每臺機器都有着自己的操作系統並保持着自己的運行。 於是,爲了將這些電腦連接起來,並能夠基於一種"通道"的形式進行數據、資源的傳輸及交互,IETF 制定了 TCP 協議。

那麼,IETF又是什麼? 這是一個令人尊敬的技術組織,叫 Internet Engineering Task Force,即互聯網工程任務組。 這是一個成立於1985年的開放性組織,現在我們所提到的 HTTP、TCP、IP 這些重要的網絡協議,都是出自於該組織。 可以這麼說,IETF 是互聯網的始作俑者,沒有它就沒有現在繁榮的互聯網了。

值得一提的是,IETF並非權貴組織,它是一個"來自民間" 的自組織、自管理的團隊,非常崇尚於自由平等的精神。

整個互聯網的底層機制是由一套標準網絡協議組成的,爲了更方便於理解,人們便定義了所謂的“網絡分層模型"。 在學習計算機網絡課程的時候,都會提到兩種網絡模型,如下:

OSI 模型,全稱爲 Open System Interconnection,即開放系統互聯模型,這個是由 ISO(International Organization for Standardization) 國際標準化組織提出的。 它主要是用來解決當時各個網絡技術供應商在協議上無法統一的問題,通過將整個網絡體系結構抽象爲 7層,從最底層的物理層、數據鏈路層一直到最上面的應用層都做了定義。

TCP/IP,即 TCP/IP Protocol Suite(協議套件)是一個以TCP協議和IP協議爲核心的通信模型,該模型採用協議堆棧的方式來實現許多通信協議,並將通訊體系抽象爲4層。 TCP/IP 模型最早發源於美國國防部(縮寫爲DoD)的ARPA網項目,此後就交由IETF組織來維護。

從上面的圖中可以看出,TCP/IP 基本上是OSI 模型的簡化版,當然也更加容易理解。 在網絡層以下,物理層、數據鏈路層所涉及的一些技術手段及概念都相對晦澀難懂,就比如光纜、中繼器、交換機等需要一些專業背景才能掌握通透。 對於大多數的軟件應用來說,將網絡層以下的部分統稱爲“網絡接口層" 無疑是更加簡單的。

因此,OSI 模型儘管非常完善且全面,但已經被 TCP/IP 模型所淘汰,在互聯網應用盛行的今天很少被提及。

二、TCP 協議


TCP 是整個 TCP/IP 協議族中最重要的傳輸層協議,它定義了一種面向連接的、可靠的、基於流的傳輸方式。 HTTP 是基於 TCP 的,所以說 TCP 是整個互聯網的協議其一併不爲過。 同時,我們在使用 HTTP 協議實現應用系統間的交互時,也經常免不了會與 TCP 打上交道。因此有必要了解一些基本機制。

TCP 的特點?

  • 首先,TCP 是基於連接的,也就是在進行數據傳輸之前,客戶端與服務端(或者說是通信的雙方)需要先建立一個可信的連接。 在數據傳輸結束後,再通過一種協定的方式斷開連接,由通信的雙方釋放資源。這裏涉及到的,就是常說的"三次握手"、"四次揮手"
  • 其次,TCP 是可靠的,它定義了一種數據包的"超時重傳機制",簡單說,就是每一個數據包在發送出去後的都會等待一個響應。 如果指定時間內沒有收到響應,由發送方進行一定次數的重傳來保證數據的可靠傳輸。
  • 最後,TCP 是基於流的,這是指在傳輸數據時應用層不需要關注數據包的邊界,TCP在數據傳輸時會自動根據網絡環境將數據進行緩衝、分組、合併。 這點跟基於報文的協議(UDP)是截然不同的。當然,基於流的傳輸也保證了數據收發的有序性,因此每個數據包都附帶上一個屬於當前連接的序列號。

怎麼理解全雙工?

全雙工是通訊上的術語,一般在軟件開發領域提到的並不多。 這是指數據同時在兩個方向上傳輸,TCP 是基於全雙工的可信傳輸協議。 當然 UDP 也可以實現全雙工的傳輸,但 TCP 只能實現點對點的傳輸,無法支持廣播或者多播(分組)。

TCP 的數據包如何組織?

透視一個協議的最原始的方法就是看它的數據包,一個TCP 的報文格式如下:

這裏面的字段就包括了:

源端口 表明發送端所使用的端口號,用於目標主機迴應。

目的端口 表明要連接的目標主機的端口號。

序號 表明發送的數據包的順序,一般爲上次發送包中的順序號+1。 若該數據包是整個TCP連接中的第一個包(SYN包),則該值是隨機生成的。

確認號 表明本端TCP已經接收到的數據,其值表示期待對端發送的下一個字節的序號。 實際上告訴對方,在這個序號減1以前的字節已正確接收。 若該數據包是整個TCP連接中的第一個包(SYN包),則確認號一般爲0。

數據偏移 表示以32位(4字節)爲單位的TCP分組頭的總長度(首部長度),用於確定用戶數據區的起始位置。 在沒有可變內容的情況下,TCP頭部的大小爲20字節,對應該值爲5。

標誌位 緊急標誌位(URG):開啓時表明此數據包處於緊急狀態應該優先處理 確認標誌位(ACK):開啓時表明確認號有效,否則忽略確認號 推送標誌位(PSH):開啓時表明應該儘快交付給應用進程,而不必等到緩存區填滿才推送,比如 telnet 的場景 復位標誌位(RST):開啓時表明TCP連接出現連接出現錯誤,數據包非法拒絕連接 同步標誌位(SYN):開啓時表明連接建立的標誌 終止標誌位(FIN):開啓時表明釋放一個連接

窗口大小 表明期望接受到的數據包字節數,用於擁塞控制。

校驗和 實現對TCP報文頭以及數據區進行校驗。

緊急指針 在緊急狀態下(URG打開),指出窗口中緊急數據的位置(末端)。

選項(可變) 用於支持一些特殊的變量,比如最大分組長度(MSS)。

填充 用於保證可變選項爲32 bit的整數倍。
 

一般情況下TCP 頭部爲20字節,加上20字節的 IP頭部,一個數據包至少包含40字節的頭部

三、TCP 工作流程

鏈是指鏈路,這個是物理層的概念,比如光纜光纖,或是無線的電磁波。 但這裏所說的鏈路其實是網絡連接的意思,即IP 上層的概念。 那麼,一個TCP 正常的通訊流程,會包含建鏈(建立連接)、傳輸數據、拆鏈(關閉連接),如下圖所示:

據上圖所示,在進行 TCP 進行數據傳輸時,都不可避免的會經過這兩個階段:

  • 三次握手建立連接

  • 執行數據傳輸、雙方讀寫

  • 四次揮手釋放連接

下面,重點說明下建鏈與拆鏈的過程

 四、 三次握手

在建立TCP連接時,需要經過三次交互,也成爲三次握手(HandShake)。

1、客戶端發起連接請求,發送 SYN包(SYN=i)到服務器,並進入到SYN-SEND狀態,等待服務器確認

2、服務器收到SYN包後,必須確認客戶的 SYN(ack=i+1),同時自己也發送一個SYN包(SYN=k),即SYN+ACK包,此時服務器進入SYN-RECV狀態

3、客戶端收到服務器的SYN+ACK包,向服務器發送確認報ACK(ack=k+1),此後客戶端和服務器進入ESTABLISHED狀態,雙方可以開始傳送數據。

在談論三次握手的時候,有幾個問題是需要關注的:

問題1. 爲什麼是三次握手

TCP 是可靠的傳輸,在建立連接時就應該經過兩端的確認過程,如上面的流程, 只有在三次握手的情況下,客戶端和服務端都經過了一次真正(SYN+ACK)的確認過程。這樣的連接便認爲是可信的。 此外,如果僅僅只是兩次握手,一旦網絡不穩定造成 SYN 包重傳則會直接導致重複建立連接,浪費資源。

問題2. 什麼是syn flood攻擊

syn flood 是一種經典的 ddos攻擊手段,這裏面用到了TCP 三次握手存在的漏洞。 在上面的圖中,可以看到當服務端接收到 SYN 後進入 SYN-RECV 狀態,此時的連接稱爲半連接,同時會被服務端寫入一個 半連接隊列。 想象一下,如果攻擊者在短時間內不斷的向服務端發送大量的 SYN 包而不響應,那麼服務器的 半連接隊列很快會被寫滿,從而導致無法工作。 實現 syn flood 的手段,可以通過僞造源 IP 的方式,這樣服務器的響應就永遠到達不了客戶端(握手無法完成); 當然,通過設定客戶端防火牆規則也可以達到同樣的目的。

對 syn flood 實現攔截是比較困難的,可以通過啓用 syn_cookies 的方式實現緩解,但這通常不是最佳方案。 最好的辦法是通過專業的防火牆來解決。

問題3. 半連接隊列和全連接隊列如何調優

這裏提到了一個"半連接隊列"(syns queue),與其對應的還有一個 "全連接隊列"(accept queue) 前者用於暫存未建立完全的連接,後者是連接在成功建立後進入的一個隊列。 半連接隊列默認大小可以通過內核參數調整:
 

echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog

 tcp_max_syn_backlog 在 syn_cookies 開啓時是無效的,這兩個選項存在衝突

對於全連接隊列,如果服務器未能及時通過 accept 調用將其中的連接取走,會導致隊列溢出(連接失效)
全連接隊列的大小的內核調優方式:

echo 4096 > /proc/sys/net/core/somaxconn

 那麼,是不是隻有內核調優這種方法能影響這兩個參數呢?答案是否定的。
實際上,在應用層調用 socket listen 時也支持設置一個 backlog參數,這幾個之間的關係如下:

半連接隊列長度 = min(backlog,內核 net.core.somaxconn,內核 tcp_max_syn_backlog)
全連接隊列長度 = min(backlog,內核 net.core.somaxconn)

一般的應用服務器如 netty、tomcat 都支持設置 backlog 參數,但是在真正進行調優時還需要配合考慮內核參數的配置。

五、 四次揮手

在釋放連接時,由於TCP是全雙工的,因此最後要由兩端分別進行關閉,這個流程如下:

1、客戶端發送一個FIN,用來關閉客戶端到服務器的數據傳送,客戶端進入FIN_WAIT_1狀態。
2、服務器收到FIN後,發送一個ACK給客戶端,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),服務器進入CLOSE_WAIT狀態,而客戶端進入FIN_WAIT2狀態。
3、服務器發送一個FIN,用來關閉服務器到客戶端的數據傳送,服務器進入LAST_ACK狀態。
4、客戶端收到FIN後,客戶端進入TIME_WAIT狀態,接着發送一個ACK給服務器,確認序號爲收到序號+1,服務器進入CLOSED狀態,完成釋放。

關閉連接有主動關閉和被動關閉一說,這裏爲了簡化理解,我們以客戶端作爲主動關閉方,服務器爲被動關閉方。

四次揮手需要關注的問題:

問題1. 爲什麼是四次揮手

發送FIN的一方就是主動關閉(客戶端),而另一方則爲被動關閉(服務器)。當一方發送了FIN,則表示在這一方不再會有數據的發送。其中當被動關閉方受到對方的FIN時,此時往往可能還有數據需要發送過去,因此無法立即發送FIN(也就是無法將FIN與ACK合併發送),而是在等待自己的數據發送完畢後再單獨發送FIN,因此整個過程需要四次交互。

問題2. 什麼是半關閉

客戶端在收到第一個FIN的ACK響應後,會進入FIN_WAIT2 狀態時,此時服務器處於 CLOSE_WAIT狀態,這種狀態就稱之爲半關閉。
從半關閉到全關閉,需要等待第二次FIN的確認纔算結束。此時,客戶端要等到服務器的FIN才能進入TIME_WAIT,
如果對方遲遲不發送FIN呢,則會等待一段時間後超時,這個可以通過內核參數tcp_fin_timeout控制,默認是60s。

問題3. 爲什麼服務器會有大量 closewait

半關閉的狀態下的服務器連接會處於 closewait 狀態,直到服務器發送了FIN。
那麼在應用層則是調用socket.close()會執行FIN的發送,如果服務器出現大量CLOSE_WAIT狀態的連接,那麼有可能的原因:

  • 服務器壓力過大,根本來不及調用close
  • 存在連接泄露問題(Bug),服務器未及時關閉連接

問題4. timewait 會帶來什麼問題

當客戶端收到了對方的FIN時,會進入TIME_WAIT狀態,此時會保持一段時間再進入CLOSE狀態。
這麼做的原因主要還是爲了可靠的關閉連接。在將TCP 進行可靠性設計之時就考慮了許多網絡的不穩定性的因素,比如:
發送給對方的ACK 可能會無法及時收到,此時對方可能重傳FIN過來,如果提前進入CLOSE則會返回RST而不是ACK,就會影響關閉流程。
因此 TIME_WAIT 狀態默認會持續一段時間,直到確認不會再有重傳的數據包之後再安全的關閉。

這裏timewait的持續時間默認是 2*MSL(總共1分鐘),這個MSL叫Max Segment Lifetime,也就是關於一個數據包在網絡中傳輸的最大生命週期的預設。
MSL默認是30s,當然這個值在現在已經可以大幅度縮減。可見在當時在設計之初,網絡狀況有多麼的糟糕。

那麼timewait會帶來什麼問題?
如果頻繁的主動關閉連接,可能會產生大量 timewait,由於timewait 的連接佔用了一個句柄及少量內存(4K),那麼就有可能會影響其他連接的建立,比如:出現 too many open files 異常..

該如何解決:

  • 重用連接,避免頻繁關閉,比如使用連接池
  • 參數調優,比如開啓tcp_tw_reuse選項支持timewait連接的重複使用。

HTTP 協議裏頭髮現了timewait的問題,於是在 HTTP 1.1 中定義了 KeepAlive 用來支持連接的重用。

問題5. RST 是什麼,爲什麼會出現

RST 是一個特殊的標記,用來表示當前應該立即終止連接。以下這些情況都會產生RST:

  • 向一個未被監聽的端口發送數據
  • 對方已經調用 close 關閉連接
  • 存在一些數據未處理(接收緩衝區),請求關閉連接時,會發送RST強制關閉
  • 某些請求發生了超時

RST 機制有時候也會被利用,做一些端口的掃描,如下:

-> 端口開啓,可接受SYN

-> 端口關閉,響應RST

 

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