網絡鏈接狀態(11種)很是重要這裏既包含三次握手中的也包括四次斷開中的,因此要熟悉。後端
三次握手過程緩存
客戶端:發送SYN=J請求創建鏈接,此時客戶端進入SYN_SENT狀態等待服務器響應bash
服務器:收到客戶端的SYN=J後發送SYN=K, ACK J+1表示收到創建鏈接請求,而後本身進入SYN_RECV狀態進行等待客戶端的最後確認服務器
客戶端:收到服務器發來的SYN=K, ACK J+1而後發送ACK=K+1表示收到以前確認,而後本身進入ESTABLISHED狀態表示本身處於鏈接創建狀態cookie
服務器:收到客戶端的ACK之後則本身進入ESTABLISHED狀態,此時雙方都處於鏈接創建狀態,以後進行數據傳送。網絡
三次握手的目的:是爲了告訴對方SEQ而後服務器回覆SEQ+1,這樣發送端就知道包沒有丟;另外握手的目的是交換信息,好比:併發
MSS:最大傳輸包(不含TCP/IP頭),MMS+包頭就是MTU,若是MTU過大傳輸就會卡死。socket
SACK_PERM:是否支持Selective ack(用戶優化重傳效率),好比客戶端發送5個包給服務器,中途丟了2號包,服務器回覆的時候只能回覆2,表示2號前面的都收到了,請求重傳2號包,但是客戶端並不知道2後面的345是否收到沒有,若是支持SACK的話,那麼服務器請求重傳2的時候就能夠同時告訴345已經收到,這樣客戶端只須要重傳2,若是沒有SACK機制,那麼客戶端就會重傳2345,這樣效率就低了。tcp
未完成鏈接隊列:客戶端發送SYN到服務器,服務器正在等待完成三次握手,此時就會把客戶端發起的這個鏈接請求放在該隊列裏,也就是sync隊列。這個隊列由net.ipv4.tcp_max_syn_backlog參數決定, 系統默認2048,服務器端口狀態爲 SYNC_RCVD。函數
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
已完成鏈接隊列:已經完成握手的鏈接從SYN隊列移動到這個隊列,也就是accept隊列,默認128(其實這個隊列最終的大小是由SOMAXCONN和使用listen函數傳入參數的二者取最小值決定的),服務器端口狀態爲ESTABLISHED,在Linux內核2.4.25以後在/etc/sysctl.conf中
net.core.somaxconn = 128直接修改。
cat /proc/sys/net/core/somaxconn
TCP的三次握手第一步服務器收到客戶端的SYN後,把該請求放在半鏈接隊列中,以後回覆SYN+ACK,當客戶端收到這個信號併發送ACK以後而且服務器正常收到和處理後就把該請求從半鏈接隊列移動到ACCEPT隊列,進入這個隊列才能從Listen變成accept。
好比syn泛洪***就是針對syn隊列的,***方不一樣的創建鏈接,可是隻作鏈接的第一步,當***者收到SYN+ACK後直接丟棄,致使受***的服務器上這個隊列滿了而後其餘正常請求就沒法進入。
常見問題:客戶端在發送完最後一個ACK以後服務器端若是收到正常狀況下應該把該連接從SYNC隊列移動到ACCEPT隊列,若是ACCEPT隊列滿了,默認服務器丟棄不會響應,因此從客戶端角度來看三次握手已經完成,但服務器沒有響應這個連接,這種狀況常常出如今服務器同時收到不少連接請求的時候。如何肯定這個問題?使用以下命令:
netstat -s | egrep "listen|LISTEN"
若是出現:
xxxxx times the listen queue of a socket overflowed(全鏈接隊列溢出次數)
xxxxx SYNs to LISTEN sockets ignored (半鏈接隊列溢出次數)
這兩個值有時你會看到同樣多,可是一般半鏈接溢出次數會大於等於全鏈接溢出次數。就說明可能會有這個問題。由於若是這個數值一直在增長那麼就要注意了。若是想再次確認,那麼你須要修改內核參數
echo '1' > /proc/sys/net/ipv4/tcp_abort_on_overflow
該參數默認爲0,參數含義看後面。修改以後客戶端再次發起鏈接就會收到reset信號,若是抓包收到這個信號,就證實服務器端的accept隊列滿了,你須要進行調整。好比JAVA中默認socket的backlog值大小是50.
ss -lnt
Send-Q:表示LISTEN端口上的全鏈接隊列最大爲多少
Recv-Q:爲全鏈接隊列當前使用了多少
全鏈接隊列大小取決於:min(backlog, somaxconn),前一個是在socket建立時傳入的(listen函數),somaxconn是OS級別的參數,這個somaxconn的含義請查看後面的內涵參數說明
半鏈接隊列大小取決於:/proc/sys/net/ipv4/tcp_max_syn_backlog 這個內核參數
Nginx默認的accept隊列是511,並且是多個進程同時監聽一個端口;Tomcat的accept隊列是100,默認短鏈接。
# 查看Accept隊列溢出狀況,若是當前沒有溢出則沒有任何返回值 netstat -s | grep TCPBacklogDrop
思考:
若是客戶端發出ACK以後恰好服務器ACCEPT隊列滿了,也就是客戶端認爲鏈接成功創建而實際上服務器端鏈接沒有準備好,而這時客戶端認爲創建好了而強行發送數據會怎麼辦呢?客戶端發送以後確定會得不到響應,由於服務器丟棄了,而後客戶端認爲丟失因此進行重傳,必定次數以後客戶端認爲異常,而後一直到超時最後斷開。
TCP鏈接客戶端connect()返回並不表明TCP鏈接成功,有多是服務器接收隊列滿了,系統會丟棄後續的ACK請求,
客戶端覺得創建了鏈接,而後就執行後續操做,而後就等待到超時。服務器則會等待ACK超時,會重傳SYN。
TCP隊列的一些問題
客戶端經過connect向服務器發出SYN包,客戶端會維護一個socket等待隊列,而服務器則會維護一個SYN隊列
此時是半鏈接狀態,若是socket等待隊列滿了,服務器則會丟棄,而客戶端會返回超時。只要客戶端沒有收到SYN+ACK,3秒後客戶端會再次發送,而後依然沒有收到,9秒後再繼續發送。
半鏈接SYN隊列長度由tcp_max_syn_backlog決定
當服務器收到客戶端SYN後,會返回SYN+ACK包,客戶端的TCP協議棧會喚醒socket等待隊列,發出connect調用
客戶端返回ACK後,服務器會進入一個新的叫作accept的隊列,這個隊列長度爲min(backlog,somaxconn)默認狀況下somaxconn是128,表示最多有129的ESTAB的鏈接等待accept(),而backlog的值由int listen(int sockfd,int backlog)中的第二個參數指定,其含義是設置listen()函數最多容許多個網絡鏈接同時處於掛起狀態,大部分平臺都是511
當accept隊列滿了以後,便是客戶端繼續向服務器發送ACK包,也不會獲得響應,此時服務器經過tcp_abort_on_overflow來決定如何返回,0表示直接丟棄,1表示發送RST通知;客戶端則會分別返回read timeout或者connection reset by peer。從上面能夠看到有2個隊列,一個保存SYN_SEND以及SYN_RECV,另一個accept隊列保存ESTAB的狀態。
好比客戶端通Nginx通訊,Nginx當即返回ACK,可是3秒後才返回響應數據,Nginx同後端通訊,發送SYN請求等待3秒後端才響應,就多是backlog值設置太小,致使accept queue溢出,SYN被丟棄致使3s重傳。
這兩個指標在不一樣場景含義不一樣。一個是狀態處於LISTEN狀態、一個是非LISTEN的其餘狀態。
LISTEN狀態
這裏的含義就是上面說的Recv-Q是當前全鏈接隊列使用量;Send-Q是當前對應進程SOCKET套接字最大blacklog的數量,也就是全鏈接隊列最大長度
非LISTEN狀態
Recv-Q:數據已經接收到本地緩存,還有多少沒有被程序取走,單位bytes
Send-Q:要發送的數據有多少還在本地緩衝區對方未確認,若是不是0多是本地發送數據過快或者對方接收數據過慢,單位bytes
上述兩個值在非LISTEN狀態下都應該保持0或者瞬間不爲0,若是長期不爲0則可能有問題。
# 統計各類狀態的值 netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' # 統計特定進程的TCP狀態 netstat -ntap | grep '3141' | awk '{++S[$6]} END {for(a in S) print a, S[a]}'
tcp_abort_on_overflow 默認爲0
TCP全鏈接隊列也就是accept隊列滿了以後如何處理,默認是0,也就是丟棄,能夠改成1,表示若是隊列滿了這時候有客戶端創建鏈接則發送一個reset包給客戶端,表示廢除這個握手。
net.core.netdev_max_backlog 默認爲128
表示當每一個網絡接口接收數據包的速率比內核處理這些包的速率快時,容許發送到隊列的數據包最大數目。就是說當接口接收包的速度比內核處理的快時,那麼多出來的數據包要存放到隊列中,那麼這個隊列最大能夠放多少個呢?就是這個參數設置的。
net.ipv4.tcp_max_orphans
用於設定系統中最多容許存在多少TCP套接字不被關聯到任何一個用戶文件句柄上,若是超過這個值,那麼沒有與用戶文件句柄關聯的TCP套接字就會被複位,同時給出警告信息。這個值主要是爲了防止DOS***。通常在系統內存比較大的狀況下能夠調大。
net.ipv4.tcp_max_syn_backlog
用於記錄還沒有收到客戶端ACK信息的鏈接請求最大值。內存比較多能夠設置大一點。也就是半鏈接的隊列,表示服務器收到了客戶端的SYN包同時服務器也發送了ACK+SYN,可是尚未收到客戶端返回的ACK包,此時鏈接處於SYN_RECV狀態,當服務器收到客戶端的ACK包時,則刪除該半鏈接條目,服務器進入ESTABLISHED狀態,這時候把該鏈接放入Accept隊列。修改這個值能夠增長更多的網絡鏈接,可是過大容易受到SYN泛洪***。
net.core.somaxconn
表示用於調節系統同時發起的TCP鏈接數,通常爲128,當高併發的狀況下,若是這個值比較小,就會致使鏈接超時或者重傳現象。Nginx服務器中定義的NGX_LISTEN_BACKLOG默認是511,因此須要調整這個參數。當服務器收到ACK包以後,就會進入一個叫accept的隊列這個隊列的最大長度就是由這個參數決定的。表示最多可有多少個ESTAB的鏈接等待accept()。這個值表示已客戶端和服務器已完成三次握手的已創建鏈接的隊列大小。
net.ipv4.tcp_timestamps
該參數用於設置時間戳,能夠避免序列號重複,在一個端口速率比較大的網卡下,遇到重複的序列號的機率仍是比較大的。若是設置爲0表示禁用對TCP時間戳的支持。默認狀況下,系統是容許重複的。可是對於Nginx來講仍是建議關閉。
net.ipv4_tcpsynack_retries
用於設置內核放棄TCP鏈接以前向客戶端發送ACK+SYN包的數量,也就是重試次數。這個參數主要影響三次握手中的第二次,也就是服務器向客戶端發送SYN+前一個SYN的ACK。通常設置爲1,表示內核放棄鏈接以前發送一次SYN+ACK包。好比客戶端發來SYN,而後服務器回覆ACK+SYN,這時候客戶端斷線了,以後會怎麼辦呢?服務器會進行重發ACK+SYNC,Linux中默認重試5次,每次時間間隔爲上一次的一倍,1s-2s-4s-8s-16s以後再等一個32s若是尚未客戶端響應,則服務器斷開這個鏈接。
net.ipv4.syn_retries
參數和上一個相似,這是此次是設置內核放棄創建鏈接以前發送SYN包的數量。也建議設置爲1.
net.ipv4.tcp.syncookies
修改此參數能夠有效防範syn flood***。原理是在TCP服務器收到SYN包後,***者就下線,這樣默認服務器須要等待63秒以後纔會斷開這個鏈接(中間服務器要重試幾回),這樣服務器的SYN隊列很快就滿了。這個參數的目的就是爲了解決這個問題,當SYN隊列滿了,服務器根據預源端口、目的IP和時間戳生產一個序列號(能夠叫作cookie)發送出去,若是是***者它是不會響應的,若是是真實請求則會返回這個cookie,而後服務器根據這個Cookie來創建鏈接就算你不在SYN隊列中也能夠。默認爲0,1表示開啓。對於鏈接請求很大的服務器不要開啓這個參數,由於它並不嚴謹。你應該設置三個參數來變相解決這個問題:net.ipv4_tcpsynack_retries、net.ipv4.tcp_max_syn_backlog和tcp_abort_on_overflow也就是,也就是減小重試次數、增大SYN隊列長度和若是處理不過來就拒絕。
net.ipv4.tcp_tw_reuse
表示開啓重用。容許將TIME_WAIT狀態的sockets從新用於新的TCP鏈接,由於大量處於TIME_WAIT狀態很浪費資源,佔用文件描述符,默認爲0,表示關閉,設置爲1表示開啓;
net.ipv4.tcp_tw_recycle
表示開啓TCP鏈接中TIME_WAIT sockets的快速回收,默認爲0,表示關閉。設置爲1表示開啓。
net.ipv4.tcp_fin_timeout
表示若是套接字由本端要求關閉,這個參數決定了它保持在FIN_WAIT-2狀態的時間。默認爲2MSL。不建議修改,若是要修改能夠根據實際狀況而定。
net.ipv4.tcp_keepalive_time
TCP keepalive心跳包機制,用於檢測鏈接是否已經斷開,這個值就是設置檢測頻率的。表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改成20分鐘。
net.ipv4.ip_local_port_range = 1024 65000
表示用於向外鏈接的端口範圍。缺省狀況下很小,改成1024到65000。
net.ipv4.tcp_max_tw_buckets = 5000表示系統同時保持TIME_WAIT套接字狀態的最大數量,若是超過這個數字,TIME_WAIT套接字將馬上被清除並打印警告信息。默認爲180000,改成5000。對於Apache、Nginx等服務器,上幾行的參數能夠很好地減小TIME_WAIT套接字數量,可是對於Squid,效果卻不大。此項參數能夠控制TIME_WAIT套接字的最大數量,避免Squid服務器被大量的TIME_WAIT套接字拖死。