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
- 混合方法
- 防火牆和代理