三次握手四次揮手

三次握手程序員

  1. 客戶端經過向服務器端發送一個SYN來建立一個主動打開,做爲三路握手的一部分。客戶端把這段鏈接的序號設定爲隨機數 A。
  2. 服務器端應當爲一個合法的SYN回送一個SYN/ACK。ACK 的確認碼應爲 A+1,SYN/ACK 包自己又有一個隨機序號 B。
  3. 最後,客戶端再發送一個ACK。當服務端受到這個ACK的時候,就完成了三路握手,並進入了鏈接建立狀態。此時包序號被設定爲收到的確認號 A+1,而響應則爲 B+1。

 

四次揮手算法

注意: 中斷鏈接端能夠是客戶端,也能夠是服務器端. 下面僅以客戶端斷開鏈接舉例, 反之亦然.後端

  1. 客戶端發送一個數據分段, 其中的 FIN 標記設置爲1. 客戶端進入 FIN-WAIT 狀態. 該狀態下客戶端只接收數據, 再也不發送數據.
  2. 服務器接收到帶有 FIN = 1 的數據分段, 發送帶有 ACK = 1 的剩餘數據分段, 確認收到客戶端發來的 FIN 信息.
  3. 服務器等到全部數據傳輸結束, 向客戶端發送一個帶有 FIN = 1 的數據分段, 並進入 CLOSE-WAIT 狀態, 等待客戶端發來帶有 ACK = 1 的確認報文.
  4. 客戶端收到服務器發來帶有 FIN = 1 的報文, 返回 ACK = 1 的報文確認, 爲了防止服務器端未收到須要重發, 進入 TIME-WAIT 狀態. 服務器接收到報文後關閉鏈接. 客戶端等待 2MSL 後未收到回覆, 則認爲服務器成功關閉, 客戶端關閉鏈接.

 

 

 

一、TIME_WAIT 狀態也稱爲2MSL等待時間
二、MSL是最大段生存時間。
三、1MSL是一個方向的存活時間=2分鐘 就是發送一個包,沒有響應的最大時間
主動發起FIN的一方是主動關閉的一方,會發送最後一個ACK(最下面的ACK),會進入TIME_WAIT,2倍MSL=4分鐘,爲了防止最後一個ACK丟失,給足時間接收另一端重傳最後的FIN。
緣由:主動關閉方的最後一個ACK有可能超時、丟包,服務器因爲沒有收到ACK,服務器會重傳FIN過來,因此會停在這個兩倍MSL
處於TIME_WAIT狀態這個TCP的五元組不能被其餘鏈接所使用,被HOLD住。
通常客戶端會執行主動關閉,服務器一般執行被動關閉不會進入TIME_WAIT狀
這兒有幾個細節須要說明的。先看三次揮手的過程當中。

客戶端第一次請求服務端,服務端收到包後會將這個數據放入到一個隊列中,叫做syn_table,以便於後期的驗證需求。接着服務器第二次收到包,也就是三次握手的第三步,進行驗證,沒問題以後放入request_sock_queue隊列中,完成三次握手。服務器

那麼這時候,所謂的syn攻擊也就是客戶端源源不斷地發送第一個包請求,而不去發送第三個確認包。服務器嘗試着進行屢次重發(能夠由tcp_synack_retries進行配置),須要大量額外的開銷。更嚴重的是,上面提到的等待隊列syn_table被佔滿,致使後來的請求再也不被服務器接收,損失慘重。cookie

想要應對這種問題呢,首先最早想到的就是加大syn_table的隊列長度咯,這個能夠經過tcp_max_syn_backlog進行配置。不過飲鴆不止渴。網絡

更好的方法,其實咱們能夠經過配置tcp_syncookies來實現,默認爲0,表示關閉,把它配成1打開它。意思是當syn等待隊列滿的時候,新來的請求不放入隊列中,而是經過cookie的方式進行處理。這話說得比較抽象。形象點描述這個算法就是,tcp在第二次握手的seq上稍做了手腳,將用戶請求的參數(包括請求的地址、端口等),加上服務器的一個序列號等作了一次運算,獲得了一個32位的無符號整數,並將這個整數做爲Seq值發送給客戶端。這個時候出現兩種情況:併發

  1. 客戶端是攻擊者:客戶端再也不作出第三次握手的響應。這個時候對服務器而言,並無任何損失,由於他不佔用服務器任何資源。
  2. 客戶端是正常的用戶:客戶端會將這個Seq的值加一做爲Ack返回給服務器。服務器拿着這個Ack值減一,進行剛纔算法的逆運算進行校驗,看是否獲得和發出去的數據一致。若是能獲得,則是一個有效的響應,不然就不是。

這要就能夠有效地防止一些syn攻擊,固然只是最初步的,等着你的還有DoS、DDoS、DRDoS。socket

接着把眼光轉移到四次揮手上面,關注兩個狀態CLOSE_WAIT,TIME_WAIT。tcp

首先來看前者,大量出現CLOSE_WAIT通常都是程序寫的有問題,也就是程序員的鍋。這種狀況下,套接字是被動關閉的。舉個例子,若是你在你的電腦上用httpclient請求服務器上一個資源,而那個資源恰好沒有,服務器會主動發出關閉鏈接的請求,那麼你就是屬於被動關閉了,而剛好你有些健忘,忘了處理httpclient的鏈接釋放,就會形成大量的CLOSE_WAIT。性能

針對這種狀況,有兩種補救措施,首先就是設置socket選項SO_REUSEADDR,表示重用端口和地址,避免不一樣的端口上堆積大量的CLOSE_WAIT。其次設置SO_LINGER,設爲0,至關於強行關閉,便不用擔憂closesocket調用進行等待完成狀態。固然這種狀況,最須要的就是review代碼。

接下來看關於TIME_WAIT的問題,這是因爲須要可靠地實現TCP全雙工鏈接的終止設計而成的,是一種正常的現象。但若是大量堆積,能夠經過一些方法來優化。

首先一樣是SO_LINGER,經過發送RST分組(而不是用正常的FIN|ACK|FIN|ACK四個分組)來關閉該鏈接,主動關閉一方的TCP狀態則跳過TIMEWAIT,直接進入CLOSED。其實這種方法是有風險的。So_REUSEADDR,解決端口重用問題,咱們能夠不用等待一整個TIME_WAIT的週期,即2*MSL,就能夠瞬時重用。

接着來講說tcp_max_tw_buckets這個參數,這個是控制併發的TIME_WAIT的數量,默認值是180000,若是超限,那麼,系統會把多的給destory掉。因而,咱們能夠經過增大這個參數來避免time_wait堆積出現的問題。固然,這有點南轅北轍。

最後要說tcp_tw_reuse和tcp_tw_recyle這兩個參數。前者表示開啓重用。容許將TIME-WAIT sockets從新用於新的TCP鏈接,只對客戶端起做用,開啓後客戶端在1s內回收。後者表示開啓TCP鏈接中TIME-WAIT sockets的快速回收,對客戶端和服務器同時起做用,開啓後在 3.5*RTO 內回收,RTO 200ms~ 120s 具體時間視網絡情況。內網情況比tw_reuse 稍快,公網尤爲移動網絡大多要比tw_reuse 慢,優勢就是可以回收服務端的TIME_WAIT數量。這二者必須在tcp_timestamps打開的時候才生效。

那麼這兒會有一個坑,若是在公司裏,大部分都使用NAT的狀況下,若是開啓tcp_tw_recyle,會出現機器連不上的狀況。

若是tcp_tw_recycle開啓,同時tcp_timestamps也開啓,tcp會記錄每一個鏈接的時間戳,若是後續時間戳比以前記錄的時間戳小,就會認爲這是錯誤的鏈接,拒絕這個鏈接。在lvs使用nat的狀況,用戶請求到lvs,LVS會修改地址數據後將請求轉發給後端服務器,但不會修改時間戳(由於nat的機制就是隻修改源地址和目的地址)。在後端服務器看來,請求的源地址永遠都是LVS的地址,而且端口複用,本來不一樣客戶端的請求通過LVS的轉發,就可能會被認爲是同一個鏈接,加之不一樣客戶端的時間可能不一致,因此就會出現時間戳錯亂的現象,因而後面的數據包就被丟棄了,就會出現部分用戶能鏈接服務器,部分用戶不能鏈接服務器的狀況。通常線上,僅開啓tcp_tw_reuse就夠了。

固然TIME_WAIT通常是短鏈接致使,某些狀況下能夠改爲長鏈接。可是長鏈接太多會影響服務器性能。

相關文章
相關標籤/搜索