偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Linux中TCP三次握手與四次揮手介紹及調(diào)優(yōu)

系統(tǒng) Linux
TCP是一種面向連接的單播協(xié)議,在發(fā)送數(shù)據(jù)前,通信雙方必須在彼此間建立一條連接。所謂的“連接”,其實(shí)是客戶端和服務(wù)器的內(nèi)存里保存的一份關(guān)于對(duì)方的信息,如ip地址、端口號(hào)等。

[[415215]]

TCP介紹

TCP是一種面向連接的單播協(xié)議,在發(fā)送數(shù)據(jù)前,通信雙方必須在彼此間建立一條連接。所謂的“連接”,其實(shí)是客戶端和服務(wù)器的內(nèi)存里保存的一份關(guān)于對(duì)方的信息,如ip地址、端口號(hào)等。

TCP可以看成是一種字節(jié)流,它會(huì)處理IP層或以下的層的丟包、重復(fù)以及錯(cuò)誤問題。在連接的建立過程中,雙方需要交換一些連接的參數(shù)。這些參數(shù)可以放在TCP頭部。

TCP提供了一種可靠、面向連接、字節(jié)流、傳輸層的服務(wù),采用三次握手建立一個(gè)連接。采用4次揮手來關(guān)閉一個(gè)連接。

TCP三次握手

客戶端和服務(wù)端通信前要進(jìn)行連接,“3次握手”的作用就是雙方都能明確自己和對(duì)方的收、發(fā)能力是正常的。

  • 第一次握手:客戶端發(fā)送網(wǎng)絡(luò)包,服務(wù)端收到了。這樣服務(wù)端就能得出結(jié)論:客戶端的發(fā)送能力、服務(wù)端的接收能力是正常的。
  • 第二次握手:服務(wù)端發(fā)包,客戶端收到了。這樣客戶端就能得出結(jié)論:服務(wù)端的接收、發(fā)送能力,客戶端的接收、發(fā)送能力是正常的。從客戶端的視角來看,我接到了服務(wù)端發(fā)送過來的響應(yīng)數(shù)據(jù)包,說明服務(wù)端接收到了我在第一次握手時(shí)發(fā)送的網(wǎng)絡(luò)包,并且成功發(fā)送了響應(yīng)數(shù)據(jù)包,這就說明,服務(wù)端的接收、發(fā)送能力正常。而另一方面,我收到了服務(wù)端的響應(yīng)數(shù)據(jù)包,說明我第一次發(fā)送的網(wǎng)絡(luò)包成功到達(dá)服務(wù)端,這樣,我自己的發(fā)送和接收能力也是正常的。
  • 第三次握手:客戶端發(fā)包,服務(wù)端收到了。這樣服務(wù)端就能得出結(jié)論:客戶端的接收、發(fā)送能力,服務(wù)端的發(fā)送、接收能力是正常的。第一、二次握手后,服務(wù)端并不知道客戶端的接收能力以及自己的發(fā)送能力是否正常。而在第三次握手時(shí),服務(wù)端收到了客戶端對(duì)第二次握手作的回應(yīng)。從服務(wù)端的角度,我在第二次握手時(shí)的響應(yīng)數(shù)據(jù)發(fā)送出去了,客戶端接收到了。所以,我的發(fā)送能力是正常的。而客戶端的接收能力也是正常的。

經(jīng)歷了上面的三次握手過程,客戶端和服務(wù)端都確認(rèn)了自己的接收、發(fā)送能力是正常的。之后就可以正常通信了。

TCP三次握手中的狀態(tài)

在 TCP 三次握手的時(shí)候,Linux 內(nèi)核會(huì)維護(hù)兩個(gè)隊(duì)列,分別是:

  • 半連接隊(duì)列,也稱 SYN 隊(duì)列;
  • 全連接隊(duì)列,也稱 accepet 隊(duì)列;

服務(wù)端收到客戶端發(fā)起的 SYN 請(qǐng)求后,內(nèi)核會(huì)把該連接存儲(chǔ)到半連接隊(duì)列,并向客戶端響應(yīng) SYN+ACK,接著客戶端會(huì)返回 ACK,服務(wù)端收到第三次握手的 ACK 后,內(nèi)核會(huì)把連接從半連接隊(duì)列移除,然后創(chuàng)建新的完全的連接,并將其添加到 accept 隊(duì)列,等待進(jìn)程調(diào)用 accept 函數(shù)時(shí)把連接取出來。

不管是半連接隊(duì)列還是全連接隊(duì)列,都有最大長度限制,超過限制時(shí),內(nèi)核會(huì)直接丟棄,或返回 RST 包。

半連接

很遺憾,我們沒有命令可以查看系統(tǒng)的半連接隊(duì)列數(shù)量。但是我們可以抓住 TCP 半連接的特點(diǎn),就是服務(wù)端處于 SYN_RECV 狀態(tài)的 TCP 連接,就是 TCP 半連接隊(duì)列。使用如下命令計(jì)算當(dāng)前 TCP 半連接隊(duì)列長度:

  1. $ netstat |grep SYN_RECV |wc -l 
  2. 1723 

SYN_RECV 狀態(tài)下,服務(wù)器必須建立一個(gè) SYN 半連接隊(duì)列來維護(hù)未完成的握手信息,當(dāng)這個(gè)隊(duì)列溢出后,服務(wù)器將無法再建立新連接。

如何模擬 TCP 半連接隊(duì)列溢出場景?

模擬 TCP 半連接溢出場景不難,實(shí)際上就是對(duì)服務(wù)端一直發(fā)送 TCP SYN 包,但是不回第三次握手 ACK,這樣就會(huì)使得服務(wù)端有大量的處于 SYN_RECV 狀態(tài)的 TCP 連接。這其實(shí)也就是所謂的 SYN 洪泛、SYN 攻擊、DDos 攻擊。

圖片

實(shí)驗(yàn)環(huán)境:

  • 客戶端和服務(wù)端都是 CentOS Linux release 7.9.2009 (Core) ,Linux 內(nèi)核版本 3.10.0-1160.15.2.el7.x86_64
  • 服務(wù)端 IP 172.16.0.20,客戶端 IP 172.16.0.157
  • 服務(wù)端是 Nginx 服務(wù),端口為 80

本次實(shí)驗(yàn)使用 hping3 工具模擬 SYN 攻擊:

  1. $ hping3 -S -p 80 --flood 172.16.0.20 
  2. HPING 172.16.0.20 (eth0 172.16.0.20): S set, 40 headers + 0 data bytes 
  3. hping in flood mode, no replies will be shown 

新連接建立失敗的原因有很多,怎樣獲得由于隊(duì)列已滿而引發(fā)的失敗次數(shù)呢?netstat -s 命令給出的統(tǒng)計(jì)結(jié)果中可以得到。

  1. $ netstat -s|grep "SYNs to LISTEN" 
  2.     1541918 SYNs to LISTEN sockets dropped 

這里給出的是隊(duì)列溢出導(dǎo)致 SYN 被丟棄的個(gè)數(shù)。注意這是一個(gè)累計(jì)值,如果數(shù)值在持續(xù)增加,則應(yīng)該調(diào)大 SYN 半連接隊(duì)列。修改隊(duì)列大小的方法,是設(shè)置 Linux 的 tcp_max_syn_backlog 參數(shù):

  1. sysctl -w net.ipv4.tcp_max_syn_backlog = 1024 

如果 SYN 半連接隊(duì)列已滿,只能丟棄連接嗎?并不是這樣,開啟 syncookies 功能就可以在不使用 SYN 隊(duì)列的情況下成功建立連接。syncookies 是這么做的:服務(wù)器根據(jù)當(dāng)前狀態(tài)計(jì)算出一個(gè)值,放在己方發(fā)出的 SYN+ACK 報(bào)文中發(fā)出,當(dāng)客戶端返回 ACK 報(bào)文時(shí),取出該值驗(yàn)證,如果合法,就認(rèn)為連接建立成功,如下圖所示。

Linux 下怎樣開啟 syncookies 功能呢?修改 tcp_syncookies 參數(shù)即可,其中值為 0 時(shí)表示關(guān)閉該功能,2 表示無條件開啟功能,而 1 則表示僅當(dāng) SYN 半連接隊(duì)列放不下時(shí),再啟用它。由于 syncookie 僅用于應(yīng)對(duì) SYN 泛洪攻擊(攻擊者惡意構(gòu)造大量的 SYN 報(bào)文發(fā)送給服務(wù)器,造成 SYN 半連接隊(duì)列溢出,導(dǎo)致正常客戶端的連接無法建立),這種方式建立的連接,許多 TCP 特性都無法使用。所以,應(yīng)當(dāng)把 tcp_syncookies 設(shè)置為 1,僅在隊(duì)列滿時(shí)再啟用。

  1. sysctl -w net.ipv4.tcp_syncookies = 1 

全連接

在服務(wù)端可以使用 ss 命令,來查看 TCP 全連接隊(duì)列的情況:但需要注意的是 ss 命令獲取的 Recv-Q/Send-Q 在「LISTEN 狀態(tài)」和「非 LISTEN 狀態(tài)」所表達(dá)的含義是不同的。從下面的內(nèi)核代碼可以看出區(qū)別:

  1. static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 
  2.          void *_info) 
  3.  const struct tcp_sock *tp = tcp_sk(sk); 
  4.  struct tcp_info *info = _info; 
  5.  
  6.  if (sk->sk_state == TCP_LISTEN) { 
  7.   r->idiag_rqueue = sk->sk_ack_backlog; 
  8.   r->idiag_wqueue = sk->sk_max_ack_backlog; 
  9.  } else { 
  10.   r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; 
  11.   r->idiag_wqueue = tp->write_seq - tp->snd_una; 
  12.  } 
  13.  if (info != NULL
  14.   tcp_get_info(sk, info); 

在「LISTEN 狀態(tài)」時(shí),Recv-Q/Send-Q 表示的含義如下:

  1. $ ss -ltnp 
  2. LISTEN     0      1024         *:8081                     *:*                   users:(("java",pid=5686,fd=310)) 
  • Recv-Q:當(dāng)前全連接隊(duì)列的大小,也就是當(dāng)前已完成三次握手并等待服務(wù)端 accept() 的 TCP 連接;
  • Send-Q:當(dāng)前全連接最大隊(duì)列長度,上面的輸出結(jié)果說明監(jiān)聽 8088 端口的 TCP 服務(wù),最大全連接長度為 1024;

在「非 LISTEN 狀態(tài)」時(shí),Recv-Q/Send-Q 表示的含義如下:

  1. $ ss -tnp 
  2. ESTAB      0      0      172.16.0.20:57672              172.16.0.20:2181                users:(("java",pid=5686,fd=292)) 
  • Recv-Q:已收到但未被應(yīng)用進(jìn)程讀取的字節(jié)數(shù);
  • Send-Q:已發(fā)送但未收到確認(rèn)的字節(jié)數(shù);

如何模擬 TCP 全連接隊(duì)列溢出的場景?

實(shí)驗(yàn)環(huán)境:

  • 客戶端和服務(wù)端都是 CentOS Linux release 7.9.2009 (Core) ,Linux 內(nèi)核版本 3.10.0-1160.15.2.el7.x86_64
  • 服務(wù)端 IP 172.16.0.20,客戶端 IP 172.16.0.157
  • 服務(wù)端是 Nginx 服務(wù),端口為 80

ab是apache bench命令的縮寫。ab是apache自帶的壓力測(cè)試工具。ab非常實(shí)用,它不僅可以對(duì)apache服務(wù)器進(jìn)行網(wǎng)站訪問壓力測(cè)試,也可以對(duì)或其它類型的服務(wù)器進(jìn)行壓力測(cè)試。比如nginx、tomcat、IIS等。ab的原理:ab命令會(huì)創(chuàng)建多個(gè)并發(fā)訪問線程,模擬多個(gè)訪問者同時(shí)對(duì)某一URL地址進(jìn)行訪問。它的測(cè)試目標(biāo)是基于URL的,因此,它既可以用來測(cè)試apache的負(fù)載壓力,也可以測(cè)試nginx、lighthttp、tomcat、IIS等其它Web服務(wù)器的壓力。ab命令對(duì)發(fā)出負(fù)載的計(jì)算機(jī)要求很低,它既不會(huì)占用很高CPU,也不會(huì)占用很多內(nèi)存。但卻會(huì)給目標(biāo)服務(wù)器造成巨大的負(fù)載,其原理類似CC攻擊。自己測(cè)試使用也需要注意,否則一次上太多的負(fù)載??赡茉斐赡繕?biāo)服務(wù)器資源耗完,嚴(yán)重時(shí)甚至導(dǎo)致死機(jī)。

TCP 全連接隊(duì)列的最大值取決于 somaxconn 和 backlog 之間的最小值,也就是 min(somaxconn, backlog)。從下面的 Linux 內(nèi)核代碼可以得知:

  1. int __sys_listen(int fd, int backlog) 
  2.  struct socket *sock; 
  3.  int err, fput_needed; 
  4.  int somaxconn; 
  5.  
  6.  sock = sockfd_lookup_light(fd, &err, &fput_needed); 
  7.  if (sock) { 
  8.   somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; 
  9.   if ((unsigned int)backlog > somaxconn) 
  10.    backlog = somaxconn; 
  11.  
  12.   err = security_socket_listen(sock, backlog); 
  13.   if (!err) 
  14.    err = sock->ops->listen(sock, backlog); 
  15.  
  16.   fput_light(sock->file, fput_needed); 
  17.  } 
  18.  return err; 
  • somaxconn 是 Linux 內(nèi)核的參數(shù),默認(rèn)值是 128,可以通過 /proc/sys/net/core/somaxconn 來設(shè)置其值;我們?cè)O(shè)置為40000了。
  • backlog 是 listen(int sockfd, int backlog) 函數(shù)中的 backlog 大小,Nginx 默認(rèn)值是 511,可以通過修改配置文件設(shè)置其長度;

所以測(cè)試環(huán)境的 TCP 全連接隊(duì)列最大值為 min(128, 511),也就是 511,可以執(zhí)行 ss 命令查看:

  1. ss -tulnp|grep 80 
  2. tcp    LISTEN     0      511       *:80                    *:*                   users:(("nginx",pid=22913,fd=6),("nginx",pid=22912,fd=6),("nginx",pid=22911,fd=6))         
  3. tcp    LISTEN     0      511    [::]:80                 [::]:*                   users:(("nginx",pid=22913,fd=7),("nginx",pid=22912,fd=7),("nginx",pid=22911,fd=7)) 

客戶端執(zhí)行 ab 命令對(duì)服務(wù)端發(fā)起壓力測(cè)試,并發(fā) 1 萬個(gè)連接,發(fā)送10萬個(gè)包:

  1. -n表示總的請(qǐng)求數(shù)為10000 
  2. -c表示并發(fā)請(qǐng)求數(shù)為1000 
  3. ab -c 10000 -n 100000 http://172.16.0.20:80/ 
  4. This is ApacheBench, Version 2.3 <$Revision: 1430300 $> 
  5. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
  6. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  7.  
  8. Benchmarking 172.16.0.20 (be patient) 
  9. Completed 10000 requests 
  10. Completed 20000 requests 
  11. Completed 30000 requests 
  12. Completed 40000 requests 
  13. Completed 50000 requests 
  14. Completed 60000 requests 
  15. Completed 70000 requests 
  16. Completed 80000 requests 
  17. Completed 90000 requests 
  18. Completed 100000 requests 
  19. Finished 100000 requests 
  20.  
  21.  
  22. Server Software:        nginx/1.20.1 
  23. Server Hostname:       172.16.0.20 
  24. Server Port:            80 
  25.  
  26. Document Path:          / 
  27. Document Length:        4833 bytes 
  28.  
  29. Concurrency Level:      10000 
  30. Time taken for tests:   2.698 seconds 
  31. Complete requests:      100000 
  32. Failed requests:        167336 
  33.    (Connect: 0, Receive: 0, Length: 84384, Exceptions: 82952) 
  34. Write errors:           0 
  35. Total transferred:      86399264 bytes 
  36. HTML transferred:       82392984 bytes 
  37. Requests per second:    37069.19 [#/sec] (mean) 
  38. Time per request:       269.766 [ms] (mean) 
  39. Time per request:       0.027 [ms] (mean, across all concurrent requests) 
  40. Transfer rate:          31276.86 [Kbytes/sec] received 
  41.  
  42. Connection Times (ms) 
  43.               min  mean[+/-sd] median   max 
  44. Connect:        0  129 151.5    106    1144 
  45. Processing:    39  121  37.7    114     239 
  46. Waiting:        0   23  51.8      0     159 
  47. Total:        142  250 152.4    224    1346 
  48.  
  49. Percentage of the requests served within a certain time (ms) 
  50.   50%    224 
  51.   66%    227 
  52.   75%    232 
  53.   80%    236 
  54.   90%    283 
  55.   95%    299 
  56.   98%   1216 
  57.   99%   1228 
  58.  100%   1346 (longest request) 

其間共執(zhí)行了兩次 ss 命令,從上面的輸出結(jié)果,可以發(fā)現(xiàn)當(dāng)前 TCP 全連接隊(duì)列上升到了 512 大小,超過了最大 TCP 全連接隊(duì)列。

  1.  ss -tulnp|grep 80 
  2. tcp    LISTEN     411    511       *:80                    *:*                   users:(("nginx",pid=22913,fd=6),("nginx",pid=22912,fd=6),("nginx",pid=22911,fd=6)) 
  3. ss -tulnp|grep 80 
  4. tcp    LISTEN     512    511       *:80                    *:*                   users:(("nginx",pid=22913,fd=6),("nginx",pid=22912,fd=6),("nginx",pid=22911,fd=6)) 

當(dāng)超過了 TCP 最大全連接隊(duì)列,服務(wù)端則會(huì)丟掉后續(xù)進(jìn)來的 TCP 連接,丟掉的 TCP 連接的個(gè)數(shù)會(huì)被統(tǒng)計(jì)起來,我們可以使用 netstat -s 命令來查看:

  1. netstat -s|grep overflowed 
  2.    1233972 times the listen queue of a socket overflowed 

上面看到的 1233972 times ,表示全連接隊(duì)列溢出的次數(shù),注意這個(gè)是累計(jì)值??梢愿魩酌腌妶?zhí)行下,如果這個(gè)數(shù)字一直在增加的話肯定全連接隊(duì)列滿了。

  1. netstat -s|grep overflowed 
  2.     1292022 times the listen queue of a socket overflowed 

從上面的模擬結(jié)果,可以得知,當(dāng)服務(wù)端并發(fā)處理大量請(qǐng)求時(shí),如果 TCP 全連接隊(duì)列過小,就容易溢出。發(fā)生 TCP 全連接隊(duì)列溢出的時(shí)候,后續(xù)的請(qǐng)求就會(huì)被丟棄,這樣就會(huì)出現(xiàn)服務(wù)端請(qǐng)求數(shù)量上不去的現(xiàn)象。

Linux 有個(gè)參數(shù)可以指定當(dāng) TCP 全連接隊(duì)列滿了會(huì)使用什么策略來回應(yīng)客戶端

實(shí)際上,丟棄連接只是 Linux 的默認(rèn)行為,我們還可以選擇向客戶端發(fā)送 RST 復(fù)位報(bào)文,告訴客戶端連接已經(jīng)建立失敗。

  1. $ cat /proc/sys/net/ipv4/tcp_abort_on_overflow 

tcp_abort_on_overflow 共有兩個(gè)值分別是 0 和 1,其分別表示:

  • 0 :如果全連接隊(duì)列滿了,那么 server 扔掉 client 發(fā)過來的 ack ;
  • 1 :如果全連接隊(duì)列滿了,server 發(fā)送一個(gè) reset 包給 client,表示廢掉這個(gè)握手過程和這個(gè)連接;

如果要想知道客戶端連接不上服務(wù)端,是不是服務(wù)端 TCP 全連接隊(duì)列滿的原因,那么可以把 tcp_abort_on_overflow 設(shè)置為 1,這時(shí)如果在客戶端異常中可以看到很多 connection reset by peer 的錯(cuò)誤,那么就可以證明是由于服務(wù)端 TCP 全連接隊(duì)列溢出的問題。通常情況下,應(yīng)當(dāng)把 tcp_abort_on_overflow 設(shè)置為 0,因?yàn)檫@樣更有利于應(yīng)對(duì)突發(fā)流量。所以,tcp_abort_on_overflow 設(shè)為 0 可以提高連接建立的成功率,只有你非??隙?TCP 全連接隊(duì)列會(huì)長期溢出時(shí),才能設(shè)置為 1 以盡快通知客戶端。

  1. sysctl -w net.ipv4.tcp_abort_on_overflow = 0 

如何增大 TCP 全連接隊(duì)列呢?

根據(jù)上面提到的TCP 全連接隊(duì)列的最大值取決于 somaxconn 和 backlog 之間的最小值,也就是 min(somaxconn, backlog)。我們現(xiàn)在調(diào)整somaxconn值:

  1. $ sysctl -w net.core.somaxconn=65535 

調(diào)整nginx配置:

  1. server { 
  2.     listen  80  backlog=65535; 
  3.     ... 

最后要重啟 Nginx 服務(wù),因?yàn)橹挥兄匦抡{(diào)用 listen() 函數(shù) TCP 全連接隊(duì)列才會(huì)重新初始化。服務(wù)端執(zhí)行 ss 命令,查看 TCP 全連接隊(duì)列大?。?/p>

  1. $ ss -tulntp|grep 80 
  2. tcp    LISTEN     0       65535    *:80                    *:*                   users:(("nginx",pid=24212,fd=6),("nginx",pid=24211,fd=6),("nginx",pid=24210,fd=6)) 

從執(zhí)行結(jié)果,可以發(fā)現(xiàn) TCP 全連接最大值為 65535。

增大 TCP 全連接隊(duì)列后,繼續(xù)壓測(cè)

  1. ab -c 10000 -n 100000 http://172.16.0.20:80/ 
  2. This is ApacheBench, Version 2.3 <$Revision: 1430300 $> 
  3. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
  4. Licensed to The Apache Software Foundation, http://www.apache.org/ 
  5.  
  6. Benchmarking 172.16.0.20 (be patient) 
  7. Completed 10000 requests 
  8. Completed 20000 requests 
  9. Completed 30000 requests 
  10. Completed 40000 requests 
  11. Completed 50000 requests 
  12. Completed 60000 requests 
  13. Completed 70000 requests 
  14. Completed 80000 requests 
  15. Completed 90000 requests 
  16. Completed 100000 requests 
  17. Finished 100000 requests 
  18.  
  19.  
  20. Server Software:        nginx/1.20.1 
  21. Server Hostname:        172.16.0.20 
  22. Server Port:            80 
  23.  
  24. Document Path:          / 
  25. Document Length:        4833 bytes 
  26.  
  27. Concurrency Level:      10000 
  28. Time taken for tests:   2.844 seconds 
  29. Complete requests:      100000 
  30. Failed requests:        178364 
  31.    (Connect: 0, Receive: 0, Length: 89728, Exceptions: 88636) 
  32. Write errors:           0 
  33. Total transferred:      57592752 bytes 
  34. HTML transferred:       54922212 bytes 
  35. Requests per second:    35159.35 [#/sec] (mean) 
  36. Time per request:       284.419 [ms] (mean) 
  37. Time per request:       0.028 [ms] (mean, across all concurrent requests) 
  38. Transfer rate:          19774.64 [Kbytes/sec] received 
  39.  
  40. Connection Times (ms) 
  41.               min  mean[+/-sd] median   max 
  42. Connect:        0  130  18.3    130     172 
  43. Processing:    45  142  40.1    138     281 
  44. Waiting:        0   19  52.4      0     185 
  45. Total:        159  272  31.2    272     390 
  46.  
  47. Percentage of the requests served within a certain time (ms) 
  48.   50%    272 
  49.   66%    274 
  50.   75%    275 
  51.   80%    276 
  52.   90%    280 
  53.   95%    358 
  54.   98%    370 
  55.   99%    375 
  56.  100%    390 (longest request) 

服務(wù)端執(zhí)行 ss 命令,查看 TCP 全連接隊(duì)列使用情況:

  1. $ ss -tulnp|grep 80 
  2. tcp    LISTEN     8      65535     *:80                    *:*                   users:(("nginx",pid=24212,fd=6),("nginx",pid=24211,fd=6),("nginx",pid=24210,fd=6)) 
  3. $ ss -tulnp|grep 80 
  4. tcp    LISTEN     352    65535     *:80                    *:*                   users:(("nginx",pid=24212,fd=6),("nginx",pid=24211,fd=6),("nginx",pid=24210,fd=6)) 
  5. $ ss -tulnp|grep 80 
  6. tcp    LISTEN     0      65535     *:80                    *:*                   users:(("nginx",pid=24212,fd=6),("nginx",pid=24211,fd=6),("nginx",pid=24210,fd=6)) 

從上面的執(zhí)行結(jié)果,可以發(fā)現(xiàn)全連接隊(duì)列使用增長的很快,但是一直都沒有超過最大值,所以就不會(huì)溢出,那么 netstat -s 就不會(huì)有 TCP 全連接隊(duì)列溢出個(gè)數(shù)繼續(xù)增加:

  1. $ netstat -s|grep overflowed 
  2.     1540879 times the listen queue of a socket overflowed 
  3. $ netstat -s|grep overflowed 
  4.     1540879 times the listen queue of a socket overflowed 
  5. $ netstat -s|grep overflowed 
  6.     1540879 times the listen queue of a socket overflowed 
  7. $ netstat -s|grep overflowed 
  8.     1540879 times the listen queue of a socket overflowed 

說明 TCP 全連接隊(duì)列最大值從 512 增大到 65535 后,服務(wù)端抗住了 10 萬連接并發(fā)請(qǐng)求,也沒有發(fā)生全連接隊(duì)列溢出的現(xiàn)象了。如果持續(xù)不斷地有連接因?yàn)?TCP 全連接隊(duì)列溢出被丟棄,就應(yīng)該調(diào)大 backlog 以及 somaxconn 參數(shù)。

TCP四次揮手

  • 第一次揮手:主動(dòng)關(guān)閉方發(fā)送一個(gè)FIN,用來關(guān)閉主動(dòng)方到被動(dòng)關(guān)閉方的數(shù)據(jù)傳送,也就是主動(dòng)關(guān)閉方告訴被動(dòng)關(guān)閉方:我已經(jīng)不會(huì)再給你發(fā)數(shù)據(jù)了(當(dāng)然,在fin包之前發(fā)送出去的數(shù)據(jù),如果沒有收到對(duì)應(yīng)的ack確認(rèn)報(bào)文,主動(dòng)關(guān)閉方依然會(huì)重發(fā)這些數(shù)據(jù)),但此時(shí)主動(dòng)關(guān)閉方還可以接受數(shù)據(jù)。
  • 第二次揮手:被動(dòng)關(guān)閉方收到FIN包后,發(fā)送一個(gè)ACK給對(duì)方,確認(rèn)序號(hào)為收到序號(hào)+1(與SYN相同,一個(gè)FIN占用一個(gè)序號(hào))。
  • 第三次揮手:被動(dòng)關(guān)閉方發(fā)送一個(gè)FIN,用來關(guān)閉被動(dòng)關(guān)閉方到主動(dòng)關(guān)閉方的數(shù)據(jù)傳送,也就是告訴主動(dòng)關(guān)閉方,我的數(shù)據(jù)也發(fā)送完了,不會(huì)再給你發(fā)數(shù)據(jù)了。
  • 第四次揮手:主動(dòng)關(guān)閉方收到FIN后,發(fā)送一個(gè)ACK給被動(dòng)關(guān)閉方,確認(rèn)序號(hào)為收到序號(hào)+1,至此,完成四次揮手。

互聯(lián)網(wǎng)中往往服務(wù)器才是主動(dòng)關(guān)閉連接的一方。這是因?yàn)?,HTTP 消息是單向傳輸協(xié)議,服務(wù)器接收完請(qǐng)求才能生成響應(yīng),發(fā)送完響應(yīng)后就會(huì)立刻關(guān)閉 TCP 連接,這樣及時(shí)釋放了資源,能夠?yàn)楦嗟挠脩舴?wù)。

四次揮手的狀態(tài)

我們來看斷開連接的時(shí)候的狀態(tài)時(shí)序圖:

其實(shí)四次揮手只涉及兩種報(bào)文:FIN 和 ACK。FIN 就是 Finish 結(jié)束連接的意思,誰發(fā)出 FIN 報(bào)文,就表示它將不再發(fā)送任何數(shù)據(jù),關(guān)閉這一方向的傳輸通道。ACK 是 Acknowledge 確認(rèn)的意思,它用來通知對(duì)方,你方的發(fā)送通道已經(jīng)關(guān)閉。

  • 當(dāng)主動(dòng)方關(guān)閉連接時(shí),會(huì)發(fā)送 FIN 報(bào)文,此時(shí)主動(dòng)方的連接狀態(tài)由 ESTABLISHED 變?yōu)?FIN_WAIT1。
  • 當(dāng)被動(dòng)方收到 FIN 報(bào)文后,內(nèi)核自動(dòng)回復(fù) ACK 報(bào)文,連接狀態(tài)由 ESTABLISHED 變?yōu)?CLOSE_WAIT,顧名思義,它在等待進(jìn)程調(diào)用 close 函數(shù)關(guān)閉連接。
  • 當(dāng)主動(dòng)方接收到這個(gè) ACK 報(bào)文后,連接狀態(tài)由 FIN_WAIT1 變?yōu)?FIN_WAIT2,主動(dòng)方的發(fā)送通道就關(guān)閉了。
  • 當(dāng)被動(dòng)方進(jìn)入 CLOSE_WAIT 狀態(tài)時(shí),進(jìn)程的 read 函數(shù)會(huì)返回 0,這樣開發(fā)人員就會(huì)有針對(duì)性地調(diào)用 close 函數(shù),進(jìn)而觸發(fā)內(nèi)核發(fā)送 FIN 報(bào)文,此時(shí)被動(dòng)方連接的狀態(tài)變?yōu)?LAST_ACK。當(dāng)主動(dòng)方收到這個(gè) FIN 報(bào)文時(shí),內(nèi)核會(huì)自動(dòng)回復(fù) ACK,同時(shí)連接的狀態(tài)由 FIN_WAIT2 變?yōu)? TIME_WAIT,Linux 系統(tǒng)下等待的時(shí)間設(shè)為 2MSL,MSL 是 Maximum Segment Lifetime,報(bào)文最大生存時(shí)間,它是任何報(bào)文在網(wǎng)絡(luò)上存在的最長時(shí)間,超過這個(gè)時(shí)間報(bào)文將被丟棄。TIME_WAIT 狀態(tài)的連接才會(huì)徹底關(guān)閉。

當(dāng)被動(dòng)方收到 ACK 報(bào)文后,連接就會(huì)關(guān)閉。

主動(dòng)方TIME_WAIT優(yōu)化

大量處于 TIME_WAIT 狀態(tài)的連接,它們會(huì)占用大量內(nèi)存和端口資源。這時(shí),我們可以優(yōu)化與 TIME_WAIT 狀態(tài)相關(guān)的內(nèi)核選項(xiàng),比如采取下面幾種措施。

  • 增大處于 TIME_WAIT 狀態(tài)的連接數(shù)量 net.ipv4.tcp_max_tw_buckets ,并增大連接跟蹤表的大小 net.netfilter.nf_conntrack_max。
  1. sysctl -w net.ipv4.tcp_max_tw_buckets=1048576 
  2. sysctl -w net.netfilter.nf_conntrack_max=1048576 
  • 減小FIN_WAIT2狀態(tài)的參數(shù) net.ipv4.tcp_fin_timeout 的時(shí)間和減小TIME_WAIT 狀態(tài)的參數(shù)net.netfilter.nf_conntrack_tcp_timeout_time_wait的時(shí)間 ,讓系統(tǒng)盡快釋放它們所占用的資源。
  1. sysctl -w net.ipv4.tcp_fin_timeout=15 
  2. sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 
  • 開啟端口復(fù)用 net.ipv4.tcp_tw_reuse。這樣,被 TIME_WAIT 狀態(tài)占用的端口,還能用到新建的連接中。
  1. sysctl -w net.ipv4.tcp_tw_reuse=1 
  • 增大本地端口的范圍 net.ipv4.ip_local_port_range 。這樣就可以支持更多連接,提高整體的并發(fā)能力。
  1. sysctl -w net.ipv4.ip_local_port_range="1024 65535" 
  • 增加最大文件描述符的數(shù)量。你可以使用 fs.nr_open 和 fs.file-max ,分別增大進(jìn)程和系統(tǒng)的最大文件描述符數(shù);或在應(yīng)用程序的 systemd 配置文件中,配置 LimitNOFILE ,設(shè)置應(yīng)用程序的最大文件描述符數(shù)。
  1. sysctl -w fs.nr_open=1048576 
  2. sysctl -w fs.file-max=1048576 

巨人的肩膀

[1] 系統(tǒng)性能調(diào)優(yōu)必知必會(huì).陶輝.極客時(shí)間.

[2] TCP/IP詳解卷二:實(shí)現(xiàn)

本文轉(zhuǎn)載自微信公眾號(hào)「 運(yùn)維開發(fā)故事」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 運(yùn)維開發(fā)故事公眾號(hào)。

【編輯推薦】

【責(zé)任編輯:姜華 TEL:(010)68476606】
責(zé)任編輯:姜華 來源: 運(yùn)維開發(fā)故事
相關(guān)推薦

2024-01-12 08:23:11

TCPACK服務(wù)器

2023-10-24 15:22:09

TCPUDP

2015-10-13 09:42:52

TCP網(wǎng)絡(luò)協(xié)議

2019-06-12 11:26:37

TCP三次握手四次揮手

2020-02-17 10:10:43

TCP三次握手四次揮手

2019-02-01 09:38:16

2021-05-18 12:27:40

TCP控制協(xié)議

2021-01-29 06:11:08

TCP通信三次握手

2021-07-03 17:47:25

TCP控制協(xié)議

2015-11-09 09:58:56

2017-09-25 21:27:07

TCP協(xié)議數(shù)據(jù)鏈

2014-09-19 09:46:46

TCPIP

2022-11-17 10:20:49

TCP三次握手四次揮手

2023-10-28 09:07:57

TCP面試三次握手

2020-06-29 14:50:47

TCP狀態(tài)ACK

2021-05-28 09:08:20

TCP連接序列號(hào)

2025-05-20 08:38:03

2020-03-02 14:41:04

運(yùn)維架構(gòu)技術(shù)

2023-11-01 08:04:08

WiresharkTCP協(xié)議

2023-10-17 15:44:19

TCP四次揮手
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)