> 若是沒有基礎的話,直接看這張圖或者網絡上各類文字描述,十分生澀,因此先看懂接下來的握手揮手的圖,理解以後,再看這個有限狀態機就感受原來如此簡單。程序員
第一次握手:主機A發送位碼爲syn=1,隨機產生seq number=x的數據包到服務器,客戶端進入SYN_SEND狀態,等待服務器的確認;主機B由SYN=1知道A要求創建鏈接。後端
第二次握手:主機B收到請求後要確認鏈接信息,向A發送ack number(主機A的seq+1)、syn=一、ack=1,隨機產生seq=y的包,此時服務器進入SYN_RECV狀態。服務器
第三次握手:主機A收到後檢查ack number是否正確,即第一次發送的seq number+1,以及位碼ack是否爲1,若正確,主機A會再發送ack number(主機B的seq+1)、ack=1,主機B收到後確認seq值與ack=1則鏈接創建成功。客戶端和服務器端都進入ESTABLISHED狀態。微信
至此,客戶端和服務端能夠肯定雙方的接收和發送能力均正常。cookie
這主要是爲了防止已失效的鏈接請求報文段忽然又傳送到了服務器端,從而減小服務端的開銷。
若是隻有兩次握手就創建鏈接會出現這種狀況:客戶端發出的鏈接請求報文段在某些網絡節點長時間滯留了,以至延誤到鏈接釋放之後的某個時間才能到達服務端。原本這是一個早已失效的報文段,但服務端收到此失效的鏈接請求報文段後,就誤認爲客戶端又發出了一次新的鏈接請求。因而向客戶端發出確認報文段,贊成創建鏈接。因爲如今客戶端並無發出創建鏈接的請求,所以不會處理服務端的確認,也不會向服務端發送數據。但服務端卻覺得新的鏈接已經創建了,並一直等待客戶端發來數據。 服務端會所以浪費不少了。網絡
服務端:
該TCP鏈接的狀態爲SYN_RECV,而且會根據TCP的超時重傳機制,會等待3秒、6秒、12秒後從新發送SYN+ACK包,以便Client從新發送ACK包。而Server重發SYN+ACK包的次數,能夠經過設置/proc/sys/net/ipv4/tcp_synack_retries修改,默認值爲5。若是重發指定次數以後,仍然未收到客戶端的ACK應答,那麼一段時間後,服務端自動關閉這個鏈接。
客戶端:
客戶端在接收到SYN+ACK包,它的TCP鏈接狀態就爲ESTABLISHED(已鏈接),表示該鏈接已經創建。那麼若是第三次握手中的ACK包丟失的狀況下,客戶端向服務端發送數據,服務端將以RST包(reset重置)響應,才能感知到服務端的錯誤。socket
syn flood是一種經典的ddos攻擊手段,這裏面用到了TCP三次握手存在的漏洞。當服務端接收到SYN後進入SYN-RECV狀態,此時的鏈接稱爲半鏈接,同時會被服務端寫入一個半鏈接隊列。若是攻擊者在短期內不斷的向服務端發送大量的SYN包而不響應,那麼服務器的半鏈接隊列很快會被寫滿,從而致使沒法工做。 實現syn flood 的手段,能夠經過僞造源IP的方式,這樣服務器的響應就永遠到達不了客戶端(握手沒法完成);固然,經過設定客戶端防火牆規則也能夠達到一樣的目的。對syn flood實現攔截是比較困難的,能夠經過啓用 syn_cookies 的方式實現緩解,但這一般不是最佳方案。最好的辦法是經過專業的防火牆來解決,基本上全部的雲計算大 都具有這個能力。tcp
第一次揮手:主機A(能夠是客戶端,也能夠是服務器端),設置Sequence Number和Acknowledgment Number,向主機B發送一個FIN報文段;此時,主機A進入FIN_WAIT_1狀態;這表示主機A沒有數據要發送給主機B了。
第二次揮手:主機B收到了主機A發送的FIN報文段,向主機A回一個ACK報文段,Acknowledgment Number爲Sequence Number加1,主機A進入FIN_WAIT_2狀態;主機B告訴主機A,我也沒有數據要發送了,能夠進行關閉鏈接了。
第三次揮手:主機B向主機A發送FIN報文段,請求關閉鏈接,同時主機B進入CLOSE_WAIT狀態。
第四次揮手:主機A收到主機B發送的FIN報文段,向主機B發送ACK報文段,而後主機A進入TIME_WAIT狀態;主機B收到主機A的ACK報文段之後,就關閉鏈接;此時,主機A等待2MSL後依然沒有收到回覆,則證實主機B已正常關閉,那好,主機A也能夠關閉鏈接了。學習
> 主機B發送了FIN-ACK以後,會當即啓動超時重傳計時器
主機A在發送最後一個ACK以後,會當即啓動時間等待計時器雲計算
由於當服務端收到客戶端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。可是關閉鏈接時,當服務端收到FIN報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK報文,告訴客戶端,"你發的FIN報文我收到了"。只有等到我服務端全部的報文都發送完了,我才能發送FIN報文,所以不能一塊兒發送。故須要四次揮手。
RST 是一個特殊的標記,用來表示當前應該當即終止鏈接。如下這些狀況都會產生RST:
半關閉的狀態下的服務器鏈接會處於closewait狀態,直到服務器發送了FIN。 那麼在應用層則是調用socket.close()會執行FIN的發送,若是服務器出現大量CLOSE_WAIT狀態的鏈接,那麼有可能的緣由:
爲了保證客戶端發送的最後一個ACK報文段可以到達服務器。由於這個ACK有可能丟失,從而致使處在LAST-ACK狀態的服務器收不到對FIN-ACK的確認報文。服務器會超時重傳這個FIN-ACK,接着客戶端再重傳一次確認,從新啓動時間等待計時器。最後客戶端和服務器都能正常的關閉。假設客戶端不等待2MSL,而是在發送完ACK以後直接釋放關閉,一但這個ACK丟失的話,服務器就沒法正常的進入關閉鏈接狀態。
> MSL是Maximum Segment Lifetime的英文縮寫,可譯爲「最長報文段壽命」,它是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。咱們都知道IP頭部中有個TTL字段,TTL是time to live的縮寫,可譯爲「生存時間」,這個生存時間是由源主機設置初始值表明一個IP數據包能夠通過的最大路由數,每通過一個路由器,它的值就減1,當此值爲0則數據報被丟棄,同時發送ICMP報文通知源主機。RFC793中規定MSL爲2分鐘,但這徹底是從工程上來考慮,對於如今的網絡,經常使用值30秒或1分鐘。所以TCP容許不一樣的實現可根據具體狀況使用更小的MSL值。
最後,限於筆者經驗水平有限,歡迎讀者就文中的觀點提出寶貴的建議和意見。若是想得到更多的學習資源或者想和更多的是技術愛好者一塊兒交流,能夠關注個人公衆號『全菜工程師小輝』後臺回覆關鍵詞領取學習資料、進入先後端技術交流羣和程序員副業羣。