TCP三次握手,四次揮手異常情況(坑)
TCP三次握手,四次揮手異常情況(坑)
1、三次握手
(文中client,server均是相對而言)
(1)、client第一個syn包丟失,沒有收到server的ack,則client進行持續重傳syn包。總嘗試時間為75秒。參與文獻《TCP/IP詳解卷1:協議》p178
(2)、server收到了client的syn,併發出了syn+ack包,syn+ack包丟失。
client方面,因為沒收server的。將執行情況(1);
server方面,超時時間內沒有收到client的ack包(或者資料包),會持續傳送syn+ack包;
(3)、當Client端收到Server的SYN+ACK應答後,其狀態變為ESTABLISHED,併發送ACK包給Server;
如果此時ACK在網路中丟失,那麼Server端該TCP連線的狀態為SYN_RECV,並且依次等待3秒、6秒、12秒後重新發送SYN+ACK包,以便Client重新發送ACK包,以便Client重新發送ACK包。
Server重發SYN+ACK包的次數,可以通過設定/proc/sys/net/ipv4/tcp_synack_retries修改,預設值為5。
如果重發指定次數後,仍然未收到ACK應答,那麼一段時間後,Server自動關閉這個連線。
如果此時client向server傳送資料包,server能正常接收資料。並認為連線已正常。參考:https://blog.csdn.net/zerooffdate/article/details/79359726
應用層編寫socket程式碼時,三次握手發生在client的connect,所以為了避免長時間(75秒)無響應連線,應設定為非阻塞socket,同時用select檢測設定合適的超時時間。
2、四次揮手
CLIENT SERVER
(1)、client發的FIN包丟了,對於client,因為沒收對應的ACK包,應當一直重傳(像普通包一樣),直至到達上限次數,直接關閉連線;對於server,它應該無任何感知;
(2)、server回client的ACK包丟了,對於client,將執行(1),對於server將像丟普通的ack一樣,再次收到FIN後,再發一個ACK包;
(3)、如果client收到ACK後,server直接跑路。client將永遠停留在這個狀態(半開啟狀態,就像client關閉了輸出一樣)。linux有tcp_fin_timeout這個引數,設定一個超時時間 cat /proc/sys/net/ipv4/tcp_fin_timeout檢視,預設60s,可否修改看linux具體版本; windows登錄檔有TcpTimedWaitDelay,win10預設值30s;
(4)、server發的FIN包丟了,對於server,像丟普通的包一樣,重傳。若此時client早已跑路且與其他人建立的連線,client應會不認識這個FIN包,直接回個RST包給server。
如若client沒跑路,且沒收到server的FIN包,如(3)描述;
(5)、client回覆ACK後,按道理來說,可以跑路了,但防止回覆的ACK包丟失(丟失後,server因為沒收FIN的ACK,所以會再發一個FIN),將等待2MSL(最大報文存活時間)(RFC793定義了MSL為2分鐘,Linux設定成了30s)為什麼要這有TIME_WAIT?為什麼不直接給轉成CLOSED狀態呢?主要有兩個原因:1)TIME_WAIT確保有足夠的時間讓對端收到了ACK,如果被動關閉的那方沒有收到Ack,就會觸發被動端重發Fin,一來一去正好2個MSL,2)有足夠的時間讓這個連線不會跟後面的連線混在一起(你要知道,有些自做主張的路由器會快取IP資料包,如果連線被重用了,那麼這些延遲收到的包就有可能會跟新連線混在一起),這期間如若再收到server的FIN,則再回復ACK;
題外話:
可以看到時三次握手第二步時,server收到client的SYN包並回復SYN+ACK包後,server會把這條連線放入“半連線佇列”。這邊有個問題,假設這個client是惡意的,client只發SYN包,收到SYN+ACK後不回覆,那在server方面,會一直存有這條“半連線”,client數量達到一定程式,serve就炸了。這就是所謂的SYN FLOOD攻擊;那怎麼防止這種情況呢?有下面幾種方法:(來自RFC 4987)
- 過濾
- 增加積壓
- 減少SYN-RECEIVED定時
- 複用古老的半開通TCP
- SYN快取
- SYN Cookie
- 混合方法
- 防火牆和代理