TCP常見問題總結
TCP常見問題
TCP全稱Transmission Control Protocol,即傳輸控制協議。TCP控制的內容主要包括:
- 可靠性
- 有序性
- 流量控制
- 擁塞控制
為何不在IP層對資料進行上述控制?
不在IP層實現控制是因為IP層涉及到的裝置很多,裝置之間靠IP來定址,如果在IP層實現控制,那麼涉及到的裝置都要關心很多事情,整體的傳輸效率會收到影響。
TCP所謂的連線只是雙方都維護了一個狀態
TCP協議頭
如圖:
- TCP包只有埠,沒有IP
- Seq就是Sequence Number即序號,用來解決亂序問題
- ACK就是Acknowledgement Number,即確認號,用來解決丟包的情況,告知傳送方接收到的包的序號
- 標誌位就是TCP flags,用來標記包的型別,用來控制TCP的狀態
- 視窗就是滑動視窗Sliding Window,用來進行流量控制
三次握手
目的有二:
- 確認雙方的傳送接收功能都正常
- 初始化Seq Number,SYN的全稱為Synchronize Sequence Numbers,這個序號是用來保證傳輸資料的正確性
初始序號ISN的取值
如果ISN從0開始,假設建立好連線之後傳送了第20個包之後網路斷了,client重啟了,序號又從0開始,此時服務端返回第20個包的ack客戶端就沒法處理了。
所以RFC739中認為ISN要和一個假設的時鐘繫結在一起:
ISN每四微秒加一,當超過2的32次方之後又從0開始,要四個半小時左右發生ISN迴繞。
所以ISN變成一個遞增值,真實的實現還需要加一些隨機值在裡面,防止被不法分子猜到ISN。
SYN超時如何處理
慢慢重試,階梯性重試。
Linux中就是預設重試5次,並且間隔是1s、2s、4s、8s、16s,在第五次發出後還得再等32s才能知道這次重試的結果,總共等63秒才能斷開連線。
SYN Flood攻擊
client向server傳送SYN但就是不回server,最後使得server的SYN(半連線)佇列耗盡,無法處理正常的建立連線的請求。
如何應對?
可以開啟tcp_syncookies,這樣就用不到SYN佇列了。SYN佇列滿了之後TCP根據自己的ip、埠然後向對方的ip、埠,對方SYN的序號,時間戳等一波操作生成一個特殊的序號(即cookie)發回去,如果對方是正常的client會把這個序號發回來,然後server根據這個序號建立連線。
或者調整tcp_synack_retries減少重試的次數,設定tcp_max_syn_backlog增加SYN佇列數,設定tcp_abort_on_overflow, SYN佇列滿了直接拒絕連線。
為什麼要四次揮手?
因為TCP是全雙工協議,也就是說雙方都要關閉,每一方都想對方傳送FIN和回應ACK,所以看起來就是四次。
主動關閉方的狀態是FIN_WAIT_1到FIN_WAIT_2,然後再到TIME_WAIT狀態。
被動關閉方是CLOSE_WAIT到LAST_ACK狀態。
為什麼要有TIME_WAIT?
主動斷開方在接收到被動關閉方的FIN並回復ACK之後並沒有直接進入CLOSED狀態,而是等待了2MSL。
MSL是Maximum Segment Lifetime,即報文最長生存時間,RFC793定義的MSL時間是2分鐘,Linux實際實現是30s,那麼2MSL是1分鐘。
等待2MSL會產生什麼問題?
如果伺服器主動關閉大量連線,那麼會出現大量的資源佔用,需要等到2MSL才會釋放資源。
如何解決2MSL產生的問題?
快速回收,即不等2MSL就回收,Linux的引數是tcp_tw_recycle,還有tcp_timestamps不過是預設開啟的。
重用,即開啟tcp_tw_reuse當然也是需要tcp_timestamps的。tcp_tw_reuse是用在連線發起方的,而我們的服務端基本上是被動接收連線方。
tcp_tw_reuse是發起新連線的時候可以複用超過1s的處於TIME_WAIT狀態的連線,所以對服務端壓力減小的效果甚微。它重用的是發起方處於TIME_WAIT的連線。
這裡還有一個SO_REUSEADDR,這是一個使用者態選項,而tcp_tw_reuse是一個核心選項。然後SO_REUSEADDR主要用在啟動服務的時候,如果此時埠被佔用了並且這個連線處於TIME_WAIT狀態,那麼可以重用這個埠,如果不是TIME_WAIT狀態,丟擲Address already in use。
所以對於服務端,tcp_tw_recycle和tcp_tw_reuse都不太好用。
所以服務端儘量不要主動關閉連線,把關閉連線的請求放到服務端來做,減少服務端出現TIME_WAIT狀態的連線出現的機率,提高服務端的資源利用率。
超時重傳機制是為了解決什麼問題?
解決了丟包問題。
為什麼還需要快速重傳機制?
超時重傳是靠時間來驅動的,如果網路狀況不好,超時重傳沒問題,如果網路狀況好的時候,只是恰巧丟包了,那等這麼長時間就沒必要。所以引入了快速重傳,如果傳送方連續三次收到對方相同的確認號,就可以證明網路狀況沒問題,確認是丟包了,那麼馬上重傳資料。
SACK的引入是為了解決什麼問題?
如果傳送方傳送了1、2、3、4這四個包,就2對方沒收到,然後不管是超時重傳還是快速重傳反正對方就回ACK 2,這時候是要重傳2、3、4呢,還是隻傳2?
SACK的引入就是為了解決傳送方不直到該重傳哪些資料的問題。
如圖所示,SACK就是接收方會回傳它已經接收到的資料,這樣傳送方就知道哪一些資料對方已經收到了。
D-SACK又是啥?
D-SACK是SACK的擴充套件,它利用SACK的第一段來描述重複接收的不連續的資料序號,如果第一段描述的範圍被ACK覆蓋了,說明重複了,比如我都ACK到6000了你還給我回SACK 5000-5500呢?
引數是tcp_dsack,Linux2.4之後預設開啟。
那直到重複了有什麼用呢?
- 知道重複了說明對方收到剛才那個包了,所以是回來了ACK包丟了。
- 是不是包亂序的,先發的包後到?
- 是不是自己太著急了,RTO太小了?
- 是不是被資料複製了,搶先一步呢?
滑動視窗的作用?
滑動視窗用來做流量控制,接收方告訴傳送方他還能接收多少資料,然後傳送方就可以根據這個資訊來控制資料的傳送。
如果接收方回覆的視窗一直是0怎麼辦?
TCP有一個Zero Window Probe技術,傳送方得知視窗是0之後,回去探測這個接收方到底行不行,也就是傳送ZWP包給接收方,如果多次之後都不行可以直接RST,傳送次數根據具體實現而定。
已經有滑動視窗了為什麼還要有擁塞控制?
加擁塞控制是因為TCP不僅僅就管兩端之間的情況,還需要知曉整體的網路情形。因為如果網路狀況很差,所有的連線都無腦重傳的話,網路將變的更差,更加擁堵。所以需要擁塞控制來避免這種情況的發生。
如何實現擁塞控制?
主要有以下幾個步驟:
- 慢啟動
- 擁塞避免,感覺差不多了減速看看
- 擁塞發生後,快送重傳/回覆
慢啟動,一開始初始化cwnd(congestion window)為1,然後每收到一個ACK就cwnd++,並且沒過一個RTT就cwnd = 2*cwnd。
然後到了一個閾值,也就是ssthresh(slow start threshold)的時候就進入擁塞避免階段。這個階段就是每收到一個ACK就cwnd = cwnd+1/cwnd並且每一個RTT就cwnd++。
然後一直線性增加,直到開始丟包的時候,一種是超時重傳,一種是快速重傳。
如果發生超時重傳,直接將ssthresh置為當前cwnd的一半,然後cwnd直接變為1,進入慢啟動階段。
如果是快速重傳,那麼有兩種實現,一種是TCP Tahoe,和超時重傳一樣的處理。一種是TCP Reno,這個實現是把cwnd=cwnd/2,然後把ssthresh設定為當前的cwnd。
然後進入快速恢復階段,將cwnd = cwnd + 3(因為快速重傳有三次),重傳DACK指定的包,如果再收到一個DACK則cwnd++,如果收到的是正常的ACK那麼就將cwnd設為ssthresh大小,進入擁塞避免階段。
總結
TCP是面向連線的,提供可靠、有序的傳輸並且還提供流控和擁塞控制,單獨提取出TCP層而不是在IP層實現是因為IP層有更多的裝置需要使用,加了複雜的邏輯不划算。
三次握手主要是為了定義初始序列號為了之後的傳輸打下基礎,四次揮手是因為TCP是全雙工協議,需要雙方都斷開連線。
SYN超時了就階梯性重試,如果有SYN攻擊,可以加大半連線佇列,或減少重試次數,或直接拒絕。
還提供流控和擁塞控制,單獨提取出TCP層而不是在IP層實現是因為IP層有更多的裝置需要使用,加了複雜的邏輯不划算。
三次握手主要是為了定義初始序列號為了之後的傳輸打下基礎,四次揮手是因為TCP是全雙工協議,需要雙方都斷開連線。
SYN超時了就階梯性重試,如果有SYN攻擊,可以加大半連線佇列,或減少重試次數,或直接拒絕。
TIME_WAIT是怕對方沒收到最後一個ACK,然後又發了FIN過來,並且也是等待處理網路上殘留的資料,怕影響新連線。
相關文章
- Vue 常見問題總結Vue
- mysql常見問題總結MySql
- Kubernetes 常見問題總結
- Flink 常見問題總結
- Spring Cloud中,Feign常見問題總結SpringCloud
- Cocos平臺整合AGC常見問題總結GC
- redis快取常見問題場景總結Redis快取
- MyBatis學習總結(24)——Mybatis常見問題彙總MyBatis
- Bootstrap常見問題彙總boot
- Java 常見問題彙總Java
- Redis常見問題彙總Redis
- 網路爬蟲常見問題(個人總結)爬蟲
- 【多執行緒】常見問題簡單總結執行緒
- Redis Manager 常見問題彙總Redis
- 代理IP常見問題彙總
- Webpack常見面試題總結Web面試題
- RecyclerView的使用總結以及常見問題解決方案View
- 解析SQLite中的常見問題與總結詳解SQLite
- Vue專案常見問題彙總Vue
- mybatis常見庫及問題彙總MyBatis
- 嘔心蒐集總結的15個“swoole”常見問題(一)
- TI毫米波雷達開發常見問題總結
- 前端常見面試題少量總結前端面試題
- leetcode 常見題型程式碼總結LeetCode
- 開發中常見問題總結
- 常見問題
- PC端/移動端常見的相容性問題總結
- 資料庫常見面試題總結資料庫面試題
- MySQL常見面試題總結[精讀]MySql面試題
- ES6常見面試題總結面試題
- TCP協議的常見面試題TCP協議面試題
- 自動化測試常見問題總結!(適合新手團隊)
- 恆訊科技總結:“Tiktok直播專線”客戶的常見問題
- Python列表最常見的問題【總結】Python
- golang 常見疑惑總結Golang
- python常見漏洞總結Python
- mount命令詳解及常見問題彙總
- js常見問題JS