自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

[[415215]]

TCP介紹

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

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

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

TCP三次握手

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

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

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

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

在 TCP 三次握手的時候,Linux 內(nèi)核會維護兩個隊列,分別是:

  • 半連接隊列,也稱 SYN 隊列;
  • 全連接隊列,也稱 accepet 隊列;

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

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

半連接

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

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

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

如何模擬 TCP 半連接隊列溢出場景?

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

圖片

實驗環(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

本次實驗使用 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 

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

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

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

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

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

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

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

全連接

在服務(wù)端可以使用 ss 命令,來查看 TCP 全連接隊列的情況:但需要注意的是 ss 命令獲取的 Recv-Q/Send-Q 在「LISTEN 狀態(tài)」和「非 LISTEN 狀態(tài)」所表達的含義是不同的。從下面的內(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)」時,Recv-Q/Send-Q 表示的含義如下:

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

在「非 LISTEN 狀態(tài)」時,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)用進程讀取的字節(jié)數(shù);
  • Send-Q:已發(fā)送但未收到確認(rèn)的字節(jié)數(shù);

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

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

TCP 全連接隊列的最大值取決于 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è)置其值;我們設(shè)置為40000了。
  • backlog 是 listen(int sockfd, int backlog) 函數(shù)中的 backlog 大小,Nginx 默認(rèn)值是 511,可以通過修改配置文件設(shè)置其長度;

所以測試環(huán)境的 TCP 全連接隊列最大值為 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 命令對服務(wù)端發(fā)起壓力測試,并發(fā) 1 萬個連接,發(fā)送10萬個包:

  1. -n表示總的請求數(shù)為10000 
  2. -c表示并發(fā)請求數(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 全連接隊列上升到了 512 大小,超過了最大 TCP 全連接隊列。

  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 最大全連接隊列,服務(wù)端則會丟掉后續(xù)進來的 TCP 連接,丟掉的 TCP 連接的個數(shù)會被統(tǒng)計起來,我們可以使用 netstat -s 命令來查看:

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

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

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

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

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

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

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

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

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

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

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

如何增大 TCP 全連接隊列呢?

根據(jù)上面提到的TCP 全連接隊列的最大值取決于 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ù),因為只有重新調(diào)用 listen() 函數(shù) TCP 全連接隊列才會重新初始化。服務(wù)端執(zhí)行 ss 命令,查看 TCP 全連接隊列大?。?/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 全連接隊列后,繼續(xù)壓測

  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 全連接隊列使用情況:

  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)全連接隊列使用增長的很快,但是一直都沒有超過最大值,所以就不會溢出,那么 netstat -s 就不會有 TCP 全連接隊列溢出個數(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 全連接隊列最大值從 512 增大到 65535 后,服務(wù)端抗住了 10 萬連接并發(fā)請求,也沒有發(fā)生全連接隊列溢出的現(xiàn)象了。如果持續(xù)不斷地有連接因為 TCP 全連接隊列溢出被丟棄,就應(yīng)該調(diào)大 backlog 以及 somaxconn 參數(shù)。

TCP四次揮手

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

互聯(lián)網(wǎng)中往往服務(wù)器才是主動關(guān)閉連接的一方。這是因為,HTTP 消息是單向傳輸協(xié)議,服務(wù)器接收完請求才能生成響應(yīng),發(fā)送完響應(yīng)后就會立刻關(guān)閉 TCP 連接,這樣及時釋放了資源,能夠為更多的用戶服務(wù)。

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

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

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

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

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

主動方TIME_WAIT優(yōu)化

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

  • 增大處于 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 的時間和減小TIME_WAIT 狀態(tài)的參數(shù)net.netfilter.nf_conntrack_tcp_timeout_time_wait的時間 ,讓系統(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 ,分別增大進程和系統(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)必知必會.陶輝.極客時間.

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

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

【編輯推薦】

【責(zé)任編輯:姜華 TEL:(010)68476606】
責(zé)任編輯:姜華 來源: 運維開發(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三次握手四次揮手

2021-01-29 06:11:08

TCP通信三次握手

2019-02-01 09:38:16

2021-05-18 12:27:40

TCP控制協(xié)議

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

2023-10-28 09:07:57

TCP面試三次握手

2022-11-17 10:20:49

TCP三次握手四次揮手

2021-05-28 09:08:20

TCP連接序列號

2020-06-29 14:50:47

TCP狀態(tài)ACK

2023-10-17 15:44:19

TCP四次揮手

2023-11-01 08:04:08

WiresharkTCP協(xié)議

2020-03-02 14:41:04

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

2023-03-07 08:38:23

三次握手四次揮手服務(wù)端
點贊
收藏

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