三次握手程序員
四次揮手算法
注意: 中斷鏈接端能夠是客戶端,也能夠是服務器端. 下面僅以客戶端斷開鏈接舉例, 反之亦然.後端
客戶端第一次請求服務端,服務端收到包後會將這個數據放入到一個隊列中,叫做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值發送給客戶端。這個時候出現兩種情況:併發
這要就能夠有效地防止一些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通常是短鏈接致使,某些狀況下能夠改爲長鏈接。可是長鏈接太多會影響服務器性能。