成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

被微信面麻了,問的太細節了。。。

開發 前端
上周有個讀者在面試微信的時候,被問到既然打開 net.ipv4.tcp_tw_reuse 參數可以快速復用處于 TIME_WAIT 狀態的 TCP 連接,那為什么 Linux 默認是關閉狀態呢?

[[442111]]

大家好,我是小林。

上周有個讀者在面試微信的時候,被問到既然打開 net.ipv4.tcp_tw_reuse 參數可以快速復用處于 TIME_WAIT 狀態的 TCP 連接,那為什么 Linux 默認是關閉狀態呢?

好家伙,真的問好細節!

當時看到讀者這個問題的時候,我也是一臉懵逼的,經過我的一番思考后,終于知道怎么回答這題了。

其實這題在變相問「如果 TIME_WAIT 狀態持續時間過短或者沒有,會有什么問題?」

因為開啟 tcp_tw_reuse 參數可以快速復用處于 TIME_WAIT 狀態的 TCP 連接時,相當于縮短了 TIME_WAIT 狀態的持續時間。

可能有的同學會問說,使用 tcp_tw_reuse 快速復用處于 TIME_WAIT 狀態的 TCP 連接時,是需要保證 net.ipv4.tcp_timestamps 參數是開啟的(默認是開啟的),而 tcp_timestamps 參數可以避免舊連接的延遲報文,這不是解決了沒有 TIME_WAIT 狀態時的問題了嗎?

是解決部分問題,但是不能完全解決,接下來,我跟大家聊聊這個問題。

什么是 TIME_WAIT 狀態?

TCP 四次揮手過程,如下圖:

  • 客戶端打算關閉連接,此時會發送一個 TCP 首部 FIN 標志位被置為 1 的報文,也即 FIN 報文,之后客戶端進入 FIN_WAIT_1 狀態。
  • 服務端收到該報文后,就向客戶端發送 ACK 應答報文,接著服務端進入 CLOSED_WAIT 狀態。
  • 客戶端收到服務端的 ACK 應答報文后,之后進入 FIN_WAIT_2 狀態。
  • 等待服務端處理完數據后,也向客戶端發送 FIN 報文,之后服務端進入 LAST_ACK 狀態。
  • 客戶端收到服務端的 FIN 報文后,回一個 ACK 應答報文,之后進入 TIME_WAIT 狀態
  • 服務器收到了 ACK 應答報文后,就進入了 CLOSE 狀態,至此服務端已經完成連接的關閉。
  • 客戶端在經過 2MSL 一段時間后,自動進入 CLOSE 狀態,至此客戶端也完成連接的關閉。

你可以看到,兩個方向都需要一個 FIN 和一個 ACK,因此通常被稱為四次揮手。

這里一點需要注意是:主動關閉連接的,才有 TIME_WAIT 狀態。

可以看到,TIME_WAIT 是「主動關閉方」斷開連接時的最后一個狀態,該狀態會持續 2MSL(Maximum Segment Lifetime) 時長,之后進入CLOSED 狀態。

MSL 指的是 TCP 協議中任何報文在網絡上最大的生存時間,任何超過這個時間的數據都將被丟棄。雖然 RFC 793 規定 MSL 為 2 分鐘,但是在實際實現的時候會有所不同,比如 Linux 默認為 30 秒,那么 2MSL 就是 60 秒。

MSL 是由網絡層的 IP 包中的 TTL 來保證的,TTL 是 IP 頭部的一個字段,用于設置一個數據報可經過的路由器的數量上限。報文每經過一次路由器的轉發,IP 頭部的 TTL 字段就會減 1,減到 0 時報文就被丟棄。

MSL 與 TTL 的區別:MSL 的單位是時間,而 TTL 是經過路由跳數。所以 MSL 應該要大于等于 TTL 消耗為 0 的時間,以確保報文已被自然消亡。

TTL 的值一般是 64,Linux 將 MSL 設置為 30 秒,意味著 Linux 認為數據報文經過 64 個路由器的時間不會超過 30 秒,如果超過了,就認為報文已經消失在網絡中了。

為什么要設計 TIME_WAIT 狀態?

設計 TIME_WAIT 狀態,主要有兩個原因:

  • 防止歷史連接中的數據,被后面相同四元組的連接錯誤的接收;
  • 保證「被動關閉連接」的一方,能被正確的關閉;

原因一:防止歷史連接中的數據,被后面相同四元組的連接錯誤的接收

為了能更好的理解這個原因,我們先來了解序列號(SEQ)和初始序列號(ISN)。

  • 序列號,是 TCP 一個頭部字段,標識了 TCP 發送端到 TCP 接收端的數據流的一個字節,因為 TCP 是面向字節流的可靠協議,為了保證消息的順序性和可靠性,TCP 為每個傳輸方向上的每個字節都賦予了一個編號,以便于傳輸成功后確認、丟失后重傳以及在接收端保證不會亂序。序列號是一個 32 位的無符號數,因此在到達 4G 之后再循環回到 0。
  • 初始序列號,在 TCP 建立連接的時候,客戶端和服務端都會各自生成一個初始序列號,它是基于時鐘生成的一個隨機數,來保證每個連接都擁有不同的初始序列號。初始化序列號可被視為一個 32 位的計數器,該計數器的數值每 4 微秒加 1,循環一次需要 4.55 小時。

給大家抓了一個包,下圖中的 Seq 就是序列號,其中紅色框住的分別是客戶端和服務端各自生成的初始序列號。

通過前面我們知道,序列號和初始化序列號并不是無限遞增的,會發生回繞為初始值的情況,這意味著無法根據序列號來判斷新老數據。

假設 TIME-WAIT 沒有等待時間或時間過短,被延遲的數據包抵達后會發生什么呢?

  • 服務端在關閉連接之前發送的 SEQ = 301 報文,被網絡延遲了。
  • 接著,服務端以相同的四元組重新打開了新連接,前面被延遲的 SEQ = 301 這時抵達了客戶端,而且該數據報文的序列號剛好在客戶端接收窗口內,因此客戶端會正常接收這個數據報文,但是這個數據報文是上一個連接殘留下來的,這樣就產生數據錯亂等嚴重的問題。

為了防止歷史連接中的數據,被后面相同四元組的連接錯誤的接收,因此 TCP 設計了 TIME_WAIT 狀態,狀態會持續 2MSL 時長,這個時間足以讓兩個方向上的數據包都被丟棄,使得原來連接的數據包在網絡中都自然消失,再出現的數據包一定都是新建立連接所產生的。

原因二:保證「被動關閉連接」的一方,能被正確的關閉

如果客戶端(主動關閉方)最后一次 ACK 報文(第四次揮手)在網絡中丟失了,那么按照 TCP 可靠性原則,服務端(被動關閉方)會重發 FIN 報文。

假設客戶端沒有 TIME_WAIT 狀態,而是在發完最后一次回 ACK 報文就直接進入 CLOSED 狀態,如果該 ACK 報文丟失了,服務端則重傳的 FIN 報文,而這時客戶端已經進入到關閉狀態了,在收到服務端重傳的 FIN 報文后,就會回 RST 報文。

服務端收到這個 RST 并將其解釋為一個錯誤(Connection reset by peer),這對于一個可靠的協議來說不是一個優雅的終止方式。

為了防止這種情況出現,客戶端必須等待足夠長的時間確保對端收到 ACK,如果對端沒有收到 ACK,那么就會觸發 TCP 重傳機制,服務端會重新發送一個 FIN,這樣一去一來剛好兩個 MSL 的時間。

但是你可能會說重新發送的 ACK 還是有可能丟失啊,沒錯,但 TCP 已經等待了那么長的時間了,已經算仁至義盡了。

tcp_tw_reuse 是什么?

在 Linux 操作系統下,TIME_WAIT 狀態的持續時間是 60 秒,這意味著這 60 秒內,客戶端一直會占用著這個端口。要知道,端口資源也是有限的,一般可以開啟的端口為 32768~61000 ,也可以通過如下參數設置指定范圍:

  1. net.ipv4.ip_local_port_range 

那么,如果如果主動關閉連接方的 TIME_WAIT 狀態過多,占滿了所有端口資源,則會導致無法創建新連接。

不過,Linux 操作系統提供了兩個可以系統參數來快速回收處于 TIME_WAIT 狀態的連接,這兩個參數都是默認關閉的:

  • net.ipv4.tcp_tw_reuse,如果開啟該選項的話,客戶端(連接發起方) 在調用 connect() 函數時,內核會隨機找一個 TIME_WAIT 狀態超過 1 秒的連接給新的連接復用,所以該選項只適用于連接發起方。
  • net.ipv4.tcp_tw_recycle,如果開啟該選項的話,允許處于 TIME_WAIT 狀態的連接被快速回收,該參數在 NAT 的網絡下是不安全的!詳細見這篇文章介紹:字節面試:SYN 報文什么時候情況下會被丟棄?

要使得上面這兩個參數生效,有一個前提條件,就是要打開 TCP 時間戳,即 net.ipv4.tcp_timestamps=1(默認即為 1)。

開啟了 tcp_timestamps 參數,TCP 頭部就會使用時間戳選項,它有兩個好處,一個是便于精確計算 RTT ,另一個是能防止序列號回繞(PAWS),我們先來介紹這個功能。

序列號是一個 32 位的無符號整型,上限值是 4GB,超過 4GB 后就需要將序列號回繞進行重用。這在以前網速慢的年代不會造成什么問題,但在一個速度足夠快的網絡中傳輸大量數據時,序列號的回繞時間就會變短。如果序列號回繞的時間極短,我們就會再次面臨之前延遲的報文抵達后序列號依然有效的問題。

為了解決這個問題,就需要有 TCP 時間戳。

試看下面的示例,假設 TCP 的發送窗口是 1 GB,并且使用了時間戳選項,發送方會為每個 TCP 報文分配時間戳數值,我們假設每個報文時間加 1,然后使用這個連接傳輸一個 6GB 大小的數據流。

32 位的序列號在時刻 D 和 E 之間回繞。假設在時刻B有一個報文丟失并被重傳,又假設這個報文段在網絡上繞了遠路并在時刻 F 重新出現。如果 TCP 無法識別這個繞回的報文,那么數據完整性就會遭到破壞。

使用時間戳選項能夠有效的防止上述問題,如果丟失的報文會在時刻 F 重新出現,由于它的時間戳為 2,小于最近的有效時間戳(5 或 6),因此防回繞序列號算法(PAWS)會將其丟棄。

防回繞序列號算法要求連接雙方維護最近一次收到的數據包的時間戳(Recent TSval),每收到一個新數據包都會讀取數據包中的時間戳值跟 Recent TSval 值做比較,如果發現收到的數據包中時間戳不是遞增的,則表示該數據包是過期的,就會直接丟棄這個數據包。

為什么 tcp_tw_reuse 默認是關閉的?

通過前面這么多鋪墊,終于可以說這個問題了。

開啟 tcp_tw_reuse 會有什么風險呢?我覺得會有 2 個問題。

第一個問題

我們知道開啟 tcp_tw_reuse 的同時,也需要開啟 tcp_timestamps,意味著可以用時間戳的方式有效的判斷回繞序列號的歷史報文。

但是在看我看了防回繞序列號算法的源碼后,發現對于 RST 報文的時間戳即使過期了,只要 RST 報文的序列號在對方的接收窗口內,也是能被接受的。

下面 tcp_validate_incoming 函數就是驗證接收到的 TCP 報文是否合格的函數,其中第一步就會進行 PAWS 檢查,由 tcp_paws_discard 函數負責。

  1. static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, int syn_inerr) 
  2.     struct tcp_sock *tp = tcp_sk(sk); 
  3.  
  4.     /* RFC1323: H1. Apply PAWS check first. */ 
  5.     if (tcp_fast_parse_options(sock_net(sk), skb, th, tp) && 
  6.         tp->rx_opt.saw_tstamp && 
  7.         tcp_paws_discard(sk, skb)) { 
  8.         if (!th->rst) { 
  9.             .... 
  10.             goto discard; 
  11.         } 
  12.         /* Reset is accepted even if it did not pass PAWS. */ 
  13.     } 

當 tcp_paws_discard 返回 true,就代表報文是一個歷史報文,于是就要丟棄這個報文。但是在丟掉這個報文的時候,會先判斷是不是 RST 報文,如果不是 RST 報文,才會將報文丟掉。也就是說,即使 RST 報文是一個歷史報文,并不會被丟棄。

假設有這樣的場景,如下圖:

  • 客戶端向一個還沒有被服務端監聽的端口發起了 HTTP 請求,接著服務端就會回 RST 報文給對方,很可惜的是 RST 報文被網絡阻塞了。
  • 由于客戶端遲遲沒有收到 TCP 第二次握手,于是重發了 SYN 包,與此同時服務端已經開啟了服務,監聽了對應的端口。于是接下來,客戶端和服務端就進行了 TCP 三次握手、數據傳輸(HTTP應答-響應)、四次揮手。
  • 因為客戶端開啟了 tcp_tw_reuse,于是快速復用 TIME_WAIT 狀態的端口,又與服務端建立了一個與剛才相同的四元組的連接。
  • 接著,前面被網絡延遲 RST 報文這時抵達了客戶端,而且 RST 報文的序列號在客戶端的接收窗口內,由于防回繞序列號算法不會防止過期的 RST,所以 RST 報文會被客戶端接受了,于是客戶端的連接就斷開了。

上面這個場景就是開啟 tcp_tw_reuse 風險,因為快速復用 TIME_WAIT 狀態的端口,導致新連接可能被回繞序列號的 RST 報文斷開了,而如果不跳過 TIME_WAIT 狀態,而是停留 2MSL 時長,那么這個 RST 報文就不會出現下一個新的連接。

可能大家會有這樣的疑問,為什么 PAWS 檢查要放過過期的 RST 報文。我翻了 RFC 1323 ,里面有一句提到:

It is recommended that RST segments NOT carry timestamps, and that RST segments be acceptable regardless of their timestamp. Old duplicate RST segments should be exceedingly unlikely, and their cleanup function should take precedence over timestamps.

大概的意思:建議 RST 段不攜帶時間戳,并且無論其時間戳如何,RST 段都是可接受的。老的重復的 RST 段應該是極不可能的,并且它們的清除功能應優先于時間戳。

RFC 1323 提到說收歷史的 RST 報文是極不可能,之所以有這樣的想法是因為 TIME_WAIT 狀態持續的 2MSL 時間,足以讓連接中的報文在網絡中自然消失,所以認為按正常操作來說是不會發生的,因此認為清除連接優先于時間戳。

而我前面提到的案例,是因為開啟了 tcp_tw_reuse 狀態,跳過了 TIME_WAIT 狀態,才發生的事情。

有同學會說,都經過一個 HTTP 請求了,延遲的 RST 報文竟然還會存活?

一個 HTTP 請求其實很快的,比如我下面這個抓包,只需要 0.2 秒就完成了,遠小于 MSL,所以延遲的 RST 報文存活是有可能的。

第二個問題

開啟 tcp_tw_reuse 來快速復用 TIME_WAIT 狀態的連接,如果第四次揮手的 ACK 報文丟失了,有可能會導致被動關閉連接的一方不能被正常的關閉,如下圖:

總結

tcp_tw_reuse 的作用是讓客戶端快速復用處于 TIME_WAIT 狀態的端口,相當于跳過了 TIME_WAIT 狀態,這可能會出現這樣的兩個問題:

  • 歷史 RST 報文可能會終止后面相同四元組的連接,因為 PAWS 檢查到即使 RST 是過期的,也不會丟棄。
  • 如果第四次揮手的 ACK 報文丟失了,有可能被動關閉連接的一方不能被正常的關閉;

雖然 TIME_WAIT 狀態持續的時間是有一點長,顯得很不友好,但是它被設計來就是用來避免發生亂七八糟的事情。

 

《UNIX網絡編程》一書中卻說道:TIME_WAIT 是我們的朋友,它是有助于我們的,不要試圖避免這個狀態,而是應該弄清楚它。

 

責任編輯:武曉燕 來源: 小林coding
相關推薦

2023-11-03 08:10:49

ThreadLoca內存泄露

2023-09-26 08:36:31

ES6 模塊JS 題目

2023-11-21 20:28:02

C++Pythonweb開發

2020-10-13 16:30:31

語言鏈表數組

2023-12-18 08:03:56

并發編程Java

2025-03-28 08:53:51

2013-07-03 09:40:04

微信

2020-08-18 16:12:02

蘋果微信軟件

2021-05-14 07:18:07

監控微信聊天

2021-06-27 06:28:55

微信微信派微信官網改版

2024-06-18 08:31:33

2021-09-27 05:27:21

微信微信狀態騰訊

2019-12-27 13:31:33

Talking DatAI人工智能

2021-09-27 22:34:14

微信廣告監控

2013-06-20 11:11:00

程序員經理

2024-01-15 07:05:50

開發大事務數據庫

2021-02-01 12:20:35

CyclicBarri工具面試

2020-05-14 14:54:00

GitHub星級開源

2021-01-22 09:56:50

微信微信8.0移動應用

2019-09-25 08:26:20

Python微信頭像
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线视频成人 | www.色.com| 成人免费在线观看 | 亚洲码欧美码一区二区三区 | 日韩欧美国产一区二区 | 日日干日日 | 97精品久久| 在线黄色影院 | 国产在线永久免费 | 精品亚洲一区二区三区四区五区 | 亚洲精品久久久久中文字幕欢迎你 | 亚洲天堂男人的天堂 | h视频免费在线观看 | 一区二区三区四区在线视频 | 亚洲 成人 在线 | 国产免费一区二区三区网站免费 | 在线成人av | 国产在线播放一区二区三区 | 久久中文字幕在线 | 黄色一级电影在线观看 | 亚州国产 | 久久综合一区 | 日韩一区二区三区在线 | 亚洲精品中文字幕 | 国产午夜在线观看 | 亚洲日韩视频 | 日韩在线 | 日韩欧美一区二区三区在线播放 | 欧美精品综合在线 | 日韩欧美在 | 国产999精品久久久 午夜天堂精品久久久久 | 伊人一二三 | 欧美日韩成人在线 | 成人性视频免费网站 | 在线观看av中文字幕 | 中文字幕人成乱码在线观看 | 偷拍自拍网站 | 亚洲午夜网 | 91婷婷韩国欧美一区二区 | 亚洲国产中文字幕 | 波多野结衣一区二区三区 |