日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > 單片機 > 架構師社區(qū)
[導讀]每日一句英語學習,每天進步一點點: 前言 TCP 性能的提升不僅考察 TCP 的理論知識,還考察了對于操作系統提供的內核參數的理解與應用。 TCP 協議是由操作系統實現,所以操作系統提供了不少調節(jié) TCP 的參數。 Linux TCP 參數 如何正確有效的使用這些參數,來

每日一句英語學習,每天進步一點點:
面試官:換人!他連 TCP 這幾個參數都不懂

前言

TCP 性能的提升不僅考察 TCP 的理論知識,還考察了對于操作系統提供的內核參數的理解與應用。

TCP 協議是由操作系統實現,所以操作系統提供了不少調節(jié) TCP 的參數。

面試官:換人!他連 TCP 這幾個參數都不懂
Linux TCP 參數

如何正確有效的使用這些參數,來提高 TCP 性能是一個不那么簡單事情。我們需要針對 TCP 每個階段的問題來對癥下藥,而不是病急亂投醫(yī)。

接下來,將以三個角度來闡述提升 TCP 的策略,分別是:

  • TCP 三次握手的性能提升;

  • TCP 四次揮手的性能提升;

  • TCP 數據傳輸的性能提升;


面試官:換人!他連 TCP 這幾個參數都不懂
本節(jié)提綱

正文

01 TCP 三次握手的性能提升

TCP 是面向連接的、可靠的、雙向傳輸的傳輸層通信協議,所以在傳輸數據之前需要經過三次握手才能建立連接。

面試官:換人!他連 TCP 這幾個參數都不懂
三次握手與數據傳輸

那么,三次握手的過程在一個 HTTP 請求的平均時間占比 10% 以上,在網絡狀態(tài)不佳、高并發(fā)或者遭遇 SYN 攻擊等場景中,如果不能有效正確的調節(jié)三次握手中的參數,就會對性能產生很多的影響。

如何正確有效的使用這些參數,來提高 TCP 三次握手的性能,這就需要理解「三次握手的狀態(tài)變遷」,這樣當出現問題時,先用 netstat 命令查看是哪個握手階段出現了問題,再來對癥下藥,而不是病急亂投醫(yī)。

面試官:換人!他連 TCP 這幾個參數都不懂
TCP 三次握手的狀態(tài)變遷

客戶端和服務端都可以針對三次握手優(yōu)化性能。主動發(fā)起連接的客戶端優(yōu)化相對簡單些,而服務端需要監(jiān)聽端口,屬于被動連接方,其間保持許多的中間狀態(tài),優(yōu)化方法相對復雜一些。

所以,客戶端(主動發(fā)起連接方)和服務端(被動連接方)優(yōu)化的方式是不同的,接下來分別針對客戶端和服務端優(yōu)化。

客戶端優(yōu)化

三次握手建立連接的首要目的是「同步序列號」。

只有同步了序列號才有可靠傳輸,TCP 許多特性都依賴于序列號實現,比如流量控制、丟包重傳等,這也是三次握手中的報文稱為 SYN 的原因,SYN 的全稱就叫 Synchronize Sequence Numbers(同步序列號)。

面試官:換人!他連 TCP 這幾個參數都不懂
TCP 頭部

SYN_SENT 狀態(tài)的優(yōu)化

客戶端作為主動發(fā)起連接方,首先它將發(fā)送 SYN 包,于是客戶端的連接就會處于 SYN_SENT 狀態(tài)。

客戶端在等待服務端回復的 ACK 報文,正常情況下,服務器會在幾毫秒內返回 SYN+ACK ,但如果客戶端長時間沒有收到 SYN+ACK 報文,則會重發(fā) SYN 包,重發(fā)的次數由 tcp_syn_retries 參數控制,默認是 5 次:

面試官:換人!他連 TCP 這幾個參數都不懂

通常,第一次超時重傳是在 1 秒后,第二次超時重傳是在 2 秒,第三次超時重傳是在 4 秒后,第四次超時重傳是在 8 秒后,第五次是在超時重傳 16 秒后。沒錯,每次超時的時間是上一次的 2 倍。

當第五次超時重傳后,會繼續(xù)等待 32 秒,如果仍然服務端沒有回應 ACK,客戶端就會終止三次握手。

所以,總耗時是 1+2+4+8+16+32=63 秒,大約 1 分鐘左右。

面試官:換人!他連 TCP 這幾個參數都不懂
SYN 超時重傳

你可以根據網絡的穩(wěn)定性和目標服務器的繁忙程度修改 SYN 的重傳次數,調整客戶端的三次握手時間上限。比如內網中通訊時,就可以適當調低重試次數,盡快把錯誤暴露給應用程序。

服務端優(yōu)化

當服務端收到 SYN 包后,服務端會立馬回復 SYN+ACK 包,表明確認收到了客戶端的序列號,同時也把自己的序列號發(fā)給對方。

此時,服務端出現了新連接,狀態(tài)是 SYN_RCV。在這個狀態(tài)下,Linux 內核就會建立一個「半連接隊列」來維護「未完成」的握手信息,當半連接隊列溢出后,服務端就無法再建立新的連接。

面試官:換人!他連 TCP 這幾個參數都不懂
半連接隊列與全連接隊列

SYN 攻擊,攻擊的是就是這個半連接隊列。

如何查看由于 SYN 半連接隊列已滿,而被丟棄連接的情況?

我們可以通過該 netstat -s 命令給出的統計結果中,  可以得到由于半連接隊列已滿,引發(fā)的失敗次數:

面試官:換人!他連 TCP 這幾個參數都不懂

上面輸出的數值是累計值,表示共有多少個 TCP 連接因為半連接隊列溢出而被丟棄。隔幾秒執(zhí)行幾次,如果有上升的趨勢,說明當前存在半連接隊列溢出的現象。

如何調整 SYN 半連接隊列大???

要想增大半連接隊列,不能只單純增大 tcp_max_syn_backlog 的值,還需一同增大 somaxconn 和 backlog,也就是增大 accept 隊列。否則,只單純增大 tcp_max_syn_backlog 是無效的。

增大 tcp_max_syn_backlog 和 somaxconn 的方法是修改 Linux 內核參數:

面試官:換人!他連 TCP 這幾個參數都不懂

增大 backlog 的方式,每個 Web 服務都不同,比如 Nginx 增大 backlog 的方法如下:

面試官:換人!他連 TCP 這幾個參數都不懂

最后,改變了如上這些參數后,要重啟 Nginx 服務,因為 SYN 半連接隊列和 accept 隊列都是在 listen() 初始化的。

如果 SYN 半連接隊列已滿,只能丟棄連接嗎?

并不是這樣,開啟 syncookies 功能就可以在不使用 SYN 半連接隊列的情況下成功建立連接。

syncookies 的工作原理:服務器根據當前狀態(tài)計算出一個值,放在己方發(fā)出的 SYN+ACK 報文中發(fā)出,當客戶端返回 ACK 報文時,取出該值驗證,如果合法,就認為連接建立成功,如下圖所示。

面試官:換人!他連 TCP 這幾個參數都不懂
開啟 syncookies 功能

syncookies 參數主要有以下三個值:

  • 0 值,表示關閉該功能;

  • 1 值,表示僅當 SYN 半連接隊列放不下時,再啟用它;

  • 2 值,表示無條件開啟功能;

那么在應對 SYN 攻擊時,只需要設置為 1 即可:

面試官:換人!他連 TCP 這幾個參數都不懂

SYN_RCV 狀態(tài)的優(yōu)化

當客戶端接收到服務器發(fā)來的 SYN+ACK 報文后,就會回復 ACK 給服務器,同時客戶端連接狀態(tài)從 SYN_SENT 轉換為 ESTABLISHED,表示連接建立成功。

服務器端連接成功建立的時間還要再往后,等到服務端收到客戶端的 ACK 后,服務端的連接狀態(tài)才變?yōu)?ESTABLISHED。

如果服務器沒有收到 ACK,就會重發(fā) SYN+ACK 報文,同時一直處于 SYN_RCV 狀態(tài)。

當網絡繁忙、不穩(wěn)定時,報文丟失就會變嚴重,此時應該調大重發(fā)次數。反之則可以調小重發(fā)次數。修改重發(fā)次數的方法是,調整 tcp_synack_retries 參數

面試官:換人!他連 TCP 這幾個參數都不懂

tcp_synack_retries 的默認重試次數是 5 次,與客戶端重傳 SYN 類似,它的重傳會經歷 1、2、4、8、16 秒,最后一次重傳后會繼續(xù)等待 32 秒,如果服務端仍然沒有收到 ACK,才會關閉連接,故共需要等待 63 秒。

服務器收到 ACK 后連接建立成功,此時,內核會把連接從半連接隊列移除,然后創(chuàng)建新的完全的連接,并將其添加到 accept 隊列,等待進程調用 accept 函數時把連接取出來。

如果進程不能及時地調用 accept 函數,就會造成 accept 隊列(也稱全連接隊列)溢出,最終導致建立好的 TCP 連接被丟棄。

面試官:換人!他連 TCP 這幾個參數都不懂
accept 隊列溢出

accept 隊列已滿,只能丟棄連接嗎?

丟棄連接只是 Linux 的默認行為,我們還可以選擇向客戶端發(fā)送 RST 復位報文,告訴客戶端連接已經建立失敗。打開這一功能需要將 tcp_abort_on_overflow 參數設置為 1。

面試官:換人!他連 TCP 這幾個參數都不懂

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

  • 0 :如果 accept 隊列滿了,那么 server 扔掉 client  發(fā)過來的 ack ;

  • 1 :如果 accept 隊列滿了,server 發(fā)送一個 RST 包給 client,表示廢掉這個握手過程和這個連接;

如果要想知道客戶端連接不上服務端,是不是服務端 TCP 全連接隊列滿的原因,那么可以把 tcp_abort_on_overflow 設置為 1,這時如果在客戶端異常中可以看到很多 connection reset by peer 的錯誤,那么就可以證明是由于服務端 TCP 全連接隊列溢出的問題。

通常情況下,應當把 tcp_abort_on_overflow 設置為 0,因為這樣更有利于應對突發(fā)流量。

舉個例子,當 accept 隊列滿導致服務器丟掉了 ACK,與此同時,客戶端的連接狀態(tài)卻是 ESTABLISHED,客戶端進程就在建立好的連接上發(fā)送請求。只要服務器沒有為請求回復 ACK,客戶端的請求就會被多次「重發(fā)」。如果服務器上的進程只是短暫的繁忙造成 accept 隊列滿,那么當 accept 隊列有空位時,再次接收到的請求報文由于含有 ACK,仍然會觸發(fā)服務器端成功建立連接。

面試官:換人!他連 TCP 這幾個參數都不懂
tcp_abort_on_overflow 為 0 可以應對突發(fā)流量

所以,tcp_abort_on_overflow 設為 0 可以提高連接建立的成功率,只有你非??隙?TCP 全連接隊列會長期溢出時,才能設置為 1 以盡快通知客戶端。

如何調整 accept 隊列的長度呢?

accept 隊列的長度取決于 somaxconn 和 backlog 之間的最小值,也就是 min(somaxconn, backlog),其中:

  • somaxconn 是 Linux 內核的參數,默認值是 128,可以通過 net.core.somaxconn 來設置其值;

  • backlog 是 listen(int sockfd, int backlog) 函數中的 backlog 大??;

Tomcat、Nginx、Apache 常見的 Web 服務的 backlog 默認值都是 511。

如何查看服務端進程 accept 隊列的長度?

可以通過 ss -ltn 命令查看:

面試官:換人!他連 TCP 這幾個參數都不懂

  • Recv-Q:當前 accept 隊列的大小,也就是當前已完成三次握手并等待服務端 accept() 的 TCP 連接;

  • Send-Q:accept 隊列最大長度,上面的輸出結果說明監(jiān)聽 8088 端口的 TCP 服務,accept 隊列的最大長度為 128;


如何查看由于 accept 連接隊列已滿,而被丟棄的連接?

當超過了 accept 連接隊列,服務端則會丟掉后續(xù)進來的 TCP 連接,丟掉的 TCP 連接的個數會被統計起來,我們可以使用 netstat -s 命令來查看:

面試官:換人!他連 TCP 這幾個參數都不懂

上面看到的 41150 times ,表示 accept 隊列溢出的次數,注意這個是累計值??梢愿魩酌腌妶?zhí)行下,如果這個數字一直在增加的話,說明 accept 連接隊列偶爾滿了。

如果持續(xù)不斷地有連接因為 accept 隊列溢出被丟棄,就應該調大 backlog 以及 somaxconn 參數。

如何繞過三次握手?

以上我們只是在對三次握手的過程進行優(yōu)化,接下來我們看看如何繞過三次握手發(fā)送數據。

三次握手建立連接造成的后果就是,HTTP 請求必須在一個 RTT(從客戶端到服務器一個往返的時間)后才能發(fā)送。

面試官:換人!他連 TCP 這幾個參數都不懂
常規(guī) HTTP 請求

在 Linux 3.7 內核版本之后,提供了 TCP Fast Open 功能,這個功能可以減少 TCP 連接建立的時延。

接下來說說,TCP Fast Open 功能的工作方式。

面試官:換人!他連 TCP 這幾個參數都不懂
開啟 TCP Fast Open 功能

在客戶端首次建立連接時的過程:

  1. 客戶端發(fā)送 SYN 報文,該報文包含 Fast Open 選項,且該選項的 Cookie 為空,這表明客戶端請求 Fast Open Cookie;

  2. 支持 TCP Fast Open 的服務器生成 Cookie,并將其置于 SYN-ACK 數據包中的 Fast Open 選項以發(fā)回客戶端;

  3. 客戶端收到 SYN-ACK 后,本地緩存 Fast Open 選項中的 Cookie。

所以,第一次發(fā)起 HTTP GET 請求的時候,還是需要正常的三次握手流程。

之后,如果客戶端再次向服務器建立連接時的過程:

  1. 客戶端發(fā)送 SYN 報文,該報文包含「數據」(對于非 TFO 的普通 TCP 握手過程,SYN 報文中不包含「數據」)以及此前記錄的 Cookie;

  2. 支持 TCP Fast Open 的服務器會對收到 Cookie 進行校驗:如果 Cookie 有效,服務器將在 SYN-ACK 報文中對 SYN 和「數據」進行確認,服務器隨后將「數據」遞送至相應的應用程序;如果 Cookie 無效,服務器將丟棄 SYN 報文中包含的「數據」,且其隨后發(fā)出的 SYN-ACK 報文將只確認 SYN 的對應序列號;

  3. 如果服務器接受了 SYN 報文中的「數據」,服務器可在握手完成之前發(fā)送「數據」,這就減少了握手帶來的 1 個 RTT 的時間消耗

  4. 客戶端將發(fā)送 ACK 確認服務器發(fā)回的 SYN 以及「數據」,但如果客戶端在初始的 SYN 報文中發(fā)送的「數據」沒有被確認,則客戶端將重新發(fā)送「數據」;

  5. 此后的 TCP 連接的數據傳輸過程和非 TFO 的正常情況一致。

所以,之后發(fā)起 HTTP GET 請求的時候,可以繞過三次握手,這就減少了握手帶來的 1 個 RTT 的時間消耗。

注:客戶端在請求并存儲了 Fast Open Cookie 之后,可以不斷重復 TCP Fast Open 直至服務器認為 Cookie 無效(通常為過期)。

Linux 下怎么打開 TCP Fast Open 功能呢?

在 Linux 系統中,可以通過設置 tcp_fastopn 內核參數,來打開 Fast Open 功能

面試官:換人!他連 TCP 這幾個參數都不懂

tcp_fastopn 各個值的意義:

  • 0 關閉

  • 1 作為客戶端使用 Fast Open 功能

  • 2 作為服務端使用 Fast Open 功能

  • 3 無論作為客戶端還是服務器,都可以使用 Fast Open 功能

TCP Fast Open 功能需要客戶端和服務端同時支持,才有效果。

小結

本小結主要介紹了關于優(yōu)化 TCP 三次握手的幾個 TCP 參數。

面試官:換人!他連 TCP 這幾個參數都不懂
三次握手優(yōu)化策略

客戶端的優(yōu)化

當客戶端發(fā)起 SYN 包時,可以通過 tcp_syn_retries 控制其重傳的次數。

服務端的優(yōu)化

當服務端 SYN 半連接隊列溢出后,會導致后續(xù)連接被丟棄,可以通過 netstat -s 觀察半連接隊列溢出的情況,如果 SYN 半連接隊列溢出情況比較嚴重,可以通過 tcp_max_syn_backlog、somaxconn、backlog 參數來調整 SYN 半連接隊列的大小。

服務端回復 SYN+ACK 的重傳次數由 tcp_synack_retries 參數控制。如果遭受 SYN 攻擊,應把 tcp_syncookies 參數設置為 1,表示僅在 SYN 隊列滿后開啟 syncookie 功能,可以保證正常的連接成功建立。

服務端收到客戶端返回的 ACK,會把連接移入 accpet 隊列,等待進行調用 accpet() 函數取出連接。

可以通過 ss -lnt 查看服務端進程的 accept 隊列長度,如果 accept 隊列溢出,系統默認丟棄 ACK,如果可以把 tcp_abort_on_overflow 設置為 1 ,表示用 RST 通知客戶端連接建立失敗。

如果 accpet 隊列溢出嚴重,可以通過 listen 函數的 backlog 參數和 somaxconn 系統參數提高隊列大小,accept 隊列長度取決于 min(backlog, somaxconn)。

繞過三次握手

TCP Fast Open 功能可以繞過三次握手,使得 HTTP 請求減少了 1 個 RTT 的時間,Linux 下可以通過 tcp_fastopen 開啟該功能,同時必須保證服務端和客戶端同時支持。


02 TCP 四次揮手的性能提升

接下來,我們一起看看針對 TCP 四次揮手關不連接時,如何優(yōu)化性能。

在開始之前,我們得先了解四次揮手狀態(tài)變遷的過程。

客戶端和服務端雙方都可以主動斷開連接,通常先關閉連接的一方稱為主動方,后關閉連接的一方稱為被動方。

面試官:換人!他連 TCP 這幾個參數都不懂
客戶端主動關閉

可以看到,四次揮手過程只涉及了兩種報文,分別是 FIN 和 ACK

  • FIN 就是結束連接的意思,誰發(fā)出 FIN 報文,就表示它將不會再發(fā)送任何數據,關閉這一方向上的傳輸通道;

  • ACK 就是確認的意思,用來通知對方:你方的發(fā)送通道已經關閉;

四次揮手的過程:

  • 當主動方關閉連接時,會發(fā)送 FIN 報文,此時發(fā)送方的 TCP 連接將從 ESTABLISHED 變成 FIN_WAIT1。

  • 當被動方收到 FIN 報文后,內核會自動回復 ACK 報文,連接狀態(tài)將從 ESTABLISHED 變成 CLOSE_WAIT,表示被動方在等待進程調用 close 函數關閉連接。

  • 當主動方收到這個 ACK 后,連接狀態(tài)由 FIN_WAIT1 變?yōu)?FIN_WAIT2,也就是表示主動方的發(fā)送通道就關閉了。

  • 當被動方進入 CLOSE_WAIT 時,被動方還會繼續(xù)處理數據,等到進程的 read 函數返回 0 后,應用程序就會調用 close 函數,進而觸發(fā)內核發(fā)送 FIN 報文,此時被動方的連接狀態(tài)變?yōu)?LAST_ACK。

  • 當主動方收到這個 FIN 報文后,內核會回復 ACK 報文給被動方,同時主動方的連接狀態(tài)由 FIN_WAIT2 變?yōu)?TIME_WAIT,在 Linux 系統下大約等待 1 分鐘后,TIME_WAIT 狀態(tài)的連接才會徹底關閉。

  • 當被動方收到最后的 ACK 報文后,被動方的連接就會關閉

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

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

主動關閉方和被動關閉方優(yōu)化的思路也不同,接下來分別說說如何優(yōu)化他們。

主動方的優(yōu)化

關閉的連接的方式通常有兩種,分別是 RST 報文關閉和 FIN 報文關閉。

如果進程異常退出了,內核就會發(fā)送 RST 報文來關閉,它可以不走四次揮手流程,是一個暴力關閉連接的方式。

安全關閉連接的方式必須通過四次揮手,它由進程調用 closeshutdown 函數發(fā)起 FIN 報文(shutdown 參數須傳入 SHUT_WR 或者 SHUT_RDWR 才會發(fā)送 FIN)。

調用 close 函數 和 shutdown 函數有什么區(qū)別?

調用了 close 函數意味著完全斷開連接,完全斷開不僅指無法傳輸數據,而且也不能發(fā)送數據。此時,調用了 close 函數的一方的連接叫做「孤兒連接」,如果你用 netstat -p 命令,會發(fā)現連接對應的進程名為空。

使用 close 函數關閉連接是不優(yōu)雅的。于是,就出現了一種優(yōu)雅關閉連接的 shutdown 函數,它可以控制只關閉一個方向的連接

面試官:換人!他連 TCP 這幾個參數都不懂

第二個參數決定斷開連接的方式,主要有以下三種方式:

  • SHUT_RD(0):關閉連接的「讀」這個方向,如果接收緩沖區(qū)有已接收的數據,則將會被丟棄,并且后續(xù)再收到新的數據,會對數據進行 ACK,然后悄悄地丟棄。也就是說,對端還是會接收到 ACK,在這種情況下根本不知道數據已經被丟棄了。

  • SHUT_WR(1):關閉連接的「寫」這個方向,這就是常被稱為「半關閉」的連接。如果發(fā)送緩沖區(qū)還有未發(fā)送的數據,將被立即發(fā)送出去,并發(fā)送一個 FIN 報文給對端。

  • SHUT_RDWR(2):相當于 SHUT_RD 和 SHUT_WR 操作各一次,關閉套接字的讀和寫兩個方向。

close 和 shutdown 函數都可以關閉連接,但這兩種方式關閉的連接,不只功能上有差異,控制它們的 Linux 參數也不相同。

FIN_WAIT1 狀態(tài)的優(yōu)化

主動方發(fā)送 FIN 報文后,連接就處于 FIN_WAIT1 狀態(tài),正常情況下,如果能及時收到被動方的 ACK,則會很快變?yōu)?FIN_WAIT2 狀態(tài)。

但是當遲遲收不到對方返回的 ACK 時,連接就會一直處于 FIN_WAIT1 狀態(tài)。此時,內核會定時重發(fā) FIN 報文,其中重發(fā)次數由 tcp_orphan_retries 參數控制(注意,orphan 雖然是孤兒的意思,該參數卻不只對孤兒連接有效,事實上,它對所有 FIN_WAIT1 狀態(tài)下的連接都有效),默認值是 0。

面試官:換人!他連 TCP 這幾個參數都不懂

你可能會好奇,這 0 表示幾次?實際上當為 0 時,特指 8 次,從下面的內核源碼可知:

面試官:換人!他連 TCP 這幾個參數都不懂

如果 FIN_WAIT1 狀態(tài)連接很多,我們就需要考慮降低 tcp_orphan_retries 的值,當重傳次數超過 tcp_orphan_retries 時,連接就會直接關閉掉。

對于普遍正常情況時,調低 tcp_orphan_retries 就已經可以了。如果遇到惡意攻擊,FIN 報文根本無法發(fā)送出去,這由 TCP 兩個特性導致的:

  • 首先,TCP 必須保證報文是有序發(fā)送的,FIN 報文也不例外,當發(fā)送緩沖區(qū)還有數據沒有發(fā)送時,FIN 報文也不能提前發(fā)送。

  • 其次,TCP 有流量控制功能,當接收方接收窗口為 0 時,發(fā)送方就不能再發(fā)送數據。所以,當攻擊者下載大文件時,就可以通過接收窗口設為 0 ,這就會使得 FIN 報文都無法發(fā)送出去,那么連接會一直處于 FIN_WAIT1 狀態(tài)。

解決這種問題的方法,是調整 tcp_max_orphans 參數,它定義了「孤兒連接」的最大數量

面試官:換人!他連 TCP 這幾個參數都不懂

當進程調用了 close 函數關閉連接,此時連接就會是「孤兒連接」,因為它無法在發(fā)送和接收數據。Linux 系統為了防止孤兒連接過多,導致系統資源長時間被占用,就提供了 tcp_max_orphans 參數。如果孤兒連接數量大于它,新增的孤兒連接將不再走四次揮手,而是直接發(fā)送 RST 復位報文強制關閉。

FIN_WAIT2 狀態(tài)的優(yōu)化

當主動方收到 ACK 報文后,會處于 FIN_WAIT2 狀態(tài),就表示主動方的發(fā)送通道已經關閉,接下來將等待對方發(fā)送 FIN 報文,關閉對方的發(fā)送通道。

這時,如果連接是用 shutdown 函數關閉的,連接可以一直處于 FIN_WAIT2 狀態(tài),因為它可能還可以發(fā)送或接收數據。但對于 close 函數關閉的孤兒連接,由于無法在發(fā)送和接收數據,所以這個狀態(tài)不可以持續(xù)太久,而 tcp_fin_timeout 控制了這個狀態(tài)下連接的持續(xù)時長,默認值是 60 秒:

面試官:換人!他連 TCP 這幾個參數都不懂

它意味著對于孤兒連接(調用 close 關閉的連接),如果在 60 秒后還沒有收到 FIN 報文,連接就會直接關閉。

這個 60 秒不是隨便決定的,它與 TIME_WAIT 狀態(tài)持續(xù)的時間是相同的,后面我們在來說說為什么是 60 秒。

TIME_WAIT 狀態(tài)的優(yōu)化

TIME_WAIT 是主動方四次揮手的最后一個狀態(tài),也是最常遇見的狀態(tài)。

當收到被動方發(fā)來的 FIN 報文后,主動方會立刻回復 ACK,表示確認對方的發(fā)送通道已經關閉,接著就處于 TIME_WAIT 狀態(tài)。在 Linux 系統,TIME_WAIT 狀態(tài)會持續(xù) 60 秒后才會進入關閉狀態(tài)。

TIME_WAIT 狀態(tài)的連接,在主動方看來確實快已經關閉了。然后,被動方沒有收到 ACK 報文前,還是處于 LAST_ACK 狀態(tài)。如果這個 ACK 報文沒有到達被動方,被動方就會重發(fā) FIN 報文。重發(fā)次數仍然由前面介紹過的 tcp_orphan_retries 參數控制。

TIME-WAIT 的狀態(tài)尤其重要,主要是兩個原因:

  • 防止具有相同「四元組」的「舊」數據包被收到;

  • 保證「被動關閉連接」的一方能被正確的關閉,即保證最后的 ACK 能讓被動關閉方接收,從而幫助其正常關閉;

原因一:防止舊連接的數據包

TIME-WAIT 的一個作用是防止收到歷史數據,從而導致數據錯亂的問題。

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

面試官:換人!他連 TCP 這幾個參數都不懂
接收到歷史數據的異常

  • 如上圖黃色框框服務端在關閉連接之前發(fā)送的 SEQ = 301 報文,被網絡延遲了。

  • 這時有相同端口的 TCP 連接被復用后,被延遲的 SEQ = 301 抵達了客戶端,那么客戶端是有可能正常接收這個過期的報文,這就會產生數據錯亂等嚴重的問題。

所以,TCP 就設計出了這么一個機制,經過 2MSL 這個時間,足以讓兩個方向上的數據包都被丟棄,使得原來連接的數據包在網絡中都自然消失,再出現的數據包一定都是新建立連接所產生的。

原因二:保證連接正確關閉

TIME-WAIT 的另外一個作用是等待足夠的時間以確保最后的 ACK 能讓被動關閉方接收,從而幫助其正常關閉。

假設 TIME-WAIT 沒有等待時間或時間過短,斷開連接會造成什么問題呢?

面試官:換人!他連 TCP 這幾個參數都不懂
沒有確保正常斷開的異常

  • 如上圖紅色框框客戶端四次揮手的最后一個 ACK 報文如果在網絡中被丟失了,此時如果客戶端 TIME-WAIT 過短或沒有,則就直接進入了 CLOSE 狀態(tài)了,那么服務端則會一直處在 LASE-ACK 狀態(tài)。

  • 當客戶端發(fā)起建立連接的 SYN 請求報文后,服務端會發(fā)送 RST 報文給客戶端,連接建立的過程就會被終止。

我們再回過頭來看看,為什么 TIME_WAIT 狀態(tài)要保持 60 秒呢?這與孤兒連接 FIN_WAIT2 狀態(tài)默認保留 60 秒的原理是一樣的,因為這兩個狀態(tài)都需要保持 2MSL 時長。MSL 全稱是 Maximum Segment Lifetime,它定義了一個報文在網絡中的最長生存時間(報文每經過一次路由器的轉發(fā),IP 頭部的 TTL 字段就會減 1,減到 0 時報文就被丟棄,這就限制了報文的最長存活時間)。

為什么是 2 MSL 的時長呢?這其實是相當于至少允許報文丟失一次。比如,若 ACK 在一個 MSL 內丟失,這樣被動方重發(fā)的 FIN 會在第 2 個 MSL 內到達,TIME_WAIT 狀態(tài)的連接可以應對。

為什么不是 4 或者 8 MSL 的時長呢?你可以想象一個丟包率達到百分之一的糟糕網絡,連續(xù)兩次丟包的概率只有萬分之一,這個概率實在是太小了,忽略它比解決它更具性價比。

因此,TIME_WAIT 和 FIN_WAIT2 狀態(tài)的最大時長都是 2 MSL,由于在 Linux 系統中,MSL 的值固定為 30 秒,所以它們都是 60 秒。

雖然 TIME_WAIT 狀態(tài)有存在的必要,但它畢竟會消耗系統資源。如果發(fā)起連接一方的 TIME_WAIT 狀態(tài)過多,占滿了所有端口資源,則會導致無法創(chuàng)建新連接。

  • 客戶端受端口資源限制:如果客戶端 TIME_WAIT 過多,就會導致端口資源被占用,因為端口就65536個,被占滿就會導致無法創(chuàng)建新的連接;

  • 服務端受系統資源限制:由于一個 四元組表示TCP連接,理論上服務端可以建立很多連接,服務端確實只監(jiān)聽一個端口 但是會把連接扔給處理線程,所以理論上監(jiān)聽的端口可以繼續(xù)監(jiān)聽。但是線程池處理不了那么多一直不斷的連接了。所以當服務端出現大量 TIME_WAIT 時,系統資源被占滿時,會導致處理不過來新的連接;

另外,Linux 提供了 tcp_max_tw_buckets 參數,當 TIME_WAIT 的連接數量超過該參數時,新關閉的連接就不再經歷 TIME_WAIT 而直接關閉:

面試官:換人!他連 TCP 這幾個參數都不懂

當服務器的并發(fā)連接增多時,相應地,同時處于 TIME_WAIT 狀態(tài)的連接數量也會變多,此時就應當調大 tcp_max_tw_buckets 參數,減少不同連接間數據錯亂的概率。

tcp_max_tw_buckets 也不是越大越好,畢竟內存和端口都是有限的。

有一種方式可以在建立新連接時,復用處于 TIME_WAIT 狀態(tài)的連接,那就是打開 tcp_tw_reuse 參數。但是需要注意,該參數是只用于客戶端(建立連接的發(fā)起方),因為是在調用 connect() 時起作用的,而對于服務端(被動連接方)是沒有用的。

面試官:換人!他連 TCP 這幾個參數都不懂

tcp_tw_reuse 從協議角度理解是安全可控的,可以復用處于 TIME_WAIT 的端口為新的連接所用。

什么是協議角度理解的安全可控呢?主要有兩點:

  • 只適用于連接發(fā)起方,也就是 C/S 模型中的客戶端;

  • 對應的 TIME_WAIT 狀態(tài)的連接創(chuàng)建時間超過 1 秒才可以被復用。

使用這個選項,還有一個前提,需要打開對 TCP 時間戳的支持(對方也要打開 ):

面試官:換人!他連 TCP 這幾個參數都不懂

由于引入了時間戳,它能帶來了些好處:

  • 我們在前面提到的 2MSL 問題就不復存在了,因為重復的數據包會因為時間戳過期被自然丟棄;

  • 同時,它還可以防止序列號繞回,也是因為重復的數據包會由于時間戳過期被自然丟棄;

老版本的 Linux 還提供了 tcp_tw_recycle 參數,但是當開啟了它,就有兩個坑:

  • Linux 會加快客戶端和服務端 TIME_WAIT 狀態(tài)的時間,也就是它會使得 TIME_WAIT 狀態(tài)會小于 60 秒,很容易導致數據錯亂;

  • 另外,Linux 會丟棄所有來自遠端時間戳小于上次記錄的時間戳(由同一個遠端發(fā)送的)的任何數據包。就是說要使用該選項,則必須保證數據包的時間戳是單調遞增的。那么,問題在于,此處的時間戳并不是我們通常意義上面的絕對時間,而是一個相對時間。很多情況下,我們是沒法保證時間戳單調遞增的,比如使用了 NAT,LVS 等情況;

所以,不建議設置為 1 ,建議關閉它:

面試官:換人!他連 TCP 這幾個參數都不懂

在 Linux 4.12 版本后,Linux 內核直接取消了這一參數。

另外,我們可以在程序中設置 socket 選項,來設置調用 close 關閉連接行為。

面試官:換人!他連 TCP 這幾個參數都不懂

如果l_onoff為非 0, 且l_linger值為 0,那么調用close后,會立該發(fā)送一個 RST 標志給對端,該 TCP 連接將跳過四次揮手,也就跳過了 TIME_WAIT 狀態(tài),直接關閉。

但這為跨越 TIME_WAIT 狀態(tài)提供了一個可能,不過是一個非常危險的行為,不值得提倡。

被動方的優(yōu)化

當被動方收到 FIN 報文時,內核會自動回復 ACK,同時連接處于 CLOSE_WAIT 狀態(tài),顧名思義,它表示等待應用進程調用 close 函數關閉連接。

內核沒有權利替代進程去關閉連接,因為如果主動方是通過 shutdown 關閉連接,那么它就是想在半關閉連接上接收數據或發(fā)送數據。因此,Linux 并沒有限制 CLOSE_WAIT 狀態(tài)的持續(xù)時間。

當然,大多數應用程序并不使用 shutdown 函數關閉連接。所以,當你用 netstat 命令發(fā)現大量 CLOSE_WAIT 狀態(tài)。就需要排查你的應用程序,因為可能因為應用程序出現了 Bug,read 函數返回 0 時,沒有調用 close 函數。

處于 CLOSE_WAIT 狀態(tài)時,調用了 close 函數,內核就會發(fā)出 FIN 報文關閉發(fā)送通道,同時連接進入 LAST_ACK 狀態(tài),等待主動方返回 ACK 來確認連接關閉。

如果遲遲收不到這個 ACK,內核就會重發(fā) FIN 報文,重發(fā)次數仍然由 tcp_orphan_retries 參數控制,這與主動方重發(fā) FIN 報文的優(yōu)化策略一致。

還有一點我們需要注意的,如果被動方迅速調用 close 函數,那么被動方的 ACK 和 FIN 有可能在一個報文中發(fā)送,這樣看起來,四次揮手會變成三次揮手,這只是一種特殊情況,不用在意。

如果連接雙方同時關閉連接,會怎么樣?

由于 TCP 是雙全工的協議,所以是會出現兩方同時關閉連接的現象,也就是同時發(fā)送了 FIN 報文。

此時,上面介紹的優(yōu)化策略仍然適用。兩方發(fā)送 FIN 報文時,都認為自己是主動方,所以都進入了 FIN_WAIT1 狀態(tài),FIN 報文的重發(fā)次數仍由 tcp_orphan_retries 參數控制。

面試官:換人!他連 TCP 這幾個參數都不懂
同時關閉

接下來,雙方在等待 ACK 報文的過程中,都等來了 FIN 報文。這是一種新情況,所以連接會進入一種叫做 CLOSING 的新狀態(tài),它替代了 FIN_WAIT2 狀態(tài)。接著,雙方內核回復 ACK 確認對方發(fā)送通道的關閉后,進入 TIME_WAIT 狀態(tài),等待 2MSL 的時間后,連接自動關閉。

小結

針對 TCP 四次揮手的優(yōu)化,我們需要根據主動方和被動方四次揮手狀態(tài)變化來調整系統 TCP 內核參數。

面試官:換人!他連 TCP 這幾個參數都不懂
四次揮手的優(yōu)化策略

主動方的優(yōu)化

主動發(fā)起 FIN 報文斷開連接的一方,如果遲遲沒收到對方的 ACK 回復,則會重傳 FIN 報文,重傳的次數由 tcp_orphan_retries 參數決定。

當主動方收到 ACK 報文后,連接就進入 FIN_WAIT2 狀態(tài),根據關閉的方式不同,優(yōu)化的方式也不同:

  • 如果這是 close 函數關閉的連接,那么它就是孤兒連接。如果 tcp_fin_timeout 秒內沒有收到對方的 FIN 報文,連接就直接關閉。同時,為了應對孤兒連接占用太多的資源,tcp_max_orphans 定義了最大孤兒連接的數量,超過時連接就會直接釋放。

  • 反之是 shutdown 函數關閉的連接,則不受此參數限制;

當主動方接收到 FIN 報文,并返回 ACK 后,主動方的連接進入 TIME_WAIT 狀態(tài)。這一狀態(tài)會持續(xù) 1 分鐘,為了防止 TIME_WAIT 狀態(tài)占用太多的資源,tcp_max_tw_buckets 定義了最大數量,超過時連接也會直接釋放。

當 TIME_WAIT 狀態(tài)過多時,還可以通過設置 tcp_tw_reusetcp_timestamps 為 1 ,將 TIME_WAIT 狀態(tài)的端口復用于作為客戶端的新連接,注意該參數只適用于客戶端。

被動方的優(yōu)化

被動關閉的連接方應對非常簡單,它在回復 ACK 后就進入了 CLOSE_WAIT 狀態(tài),等待進程調用 close 函數關閉連接。因此,出現大量 CLOSE_WAIT 狀態(tài)的連接時,應當從應用程序中找問題。

當被動方發(fā)送 FIN 報文后,連接就進入 LAST_ACK 狀態(tài),在未等到 ACK 時,會在 tcp_orphan_retries 參數的控制下重發(fā) FIN 報文。


03 TCP 傳輸數據的性能提升

在前面介紹的是三次握手和四次揮手的優(yōu)化策略,接下來主要介紹的是 TCP 傳輸數據時的優(yōu)化策略。

TCP 連接是由內核維護的,內核會為每個連接建立內存緩沖區(qū):

  • 如果連接的內存配置過小,就無法充分使用網絡帶寬,TCP 傳輸效率就會降低;

  • 如果連接的內存配置過大,很容易把服務器資源耗盡,這樣就會導致新連接無法建立;

因此,我們必須理解 Linux 下 TCP 內存的用途,才能正確地配置內存大小。

滑動窗口是如何影響傳輸速度的?

TCP 會保證每一個報文都能夠抵達對方,它的機制是這樣:報文發(fā)出去后,必須接收到對方返回的確認報文 ACK,如果遲遲未收到,就會超時重發(fā)該報文,直到收到對方的 ACK 為止。

所以,TCP 報文發(fā)出去后,并不會立馬從內存中刪除,因為重傳時還需要用到它。

由于 TCP 是內核維護的,所以報文存放在內核緩沖區(qū)。如果連接非常多,我們可以通過 free 命令觀察到 buff/cache 內存是會增大。

如果 TCP 是每發(fā)送一個數據,都要進行一次確認應答。當上一個數據包收到了應答了, 再發(fā)送下一個。這個模式就有點像我和你面對面聊天,你一句我一句,但這種方式的缺點是效率比較低的。

面試官:換人!他連 TCP 這幾個參數都不懂
按數據包進行確認應答

所以,這樣的傳輸方式有一個缺點:數據包的往返時間越長,通信的效率就越低

要解決這一問題不難,并行批量發(fā)送報文,再批量確認報文即刻。

面試官:換人!他連 TCP 這幾個參數都不懂
并行處理

然而,這引出了另一個問題,發(fā)送方可以隨心所欲的發(fā)送報文嗎?當然這不現實,我們還得考慮接收方的處理能力。

當接收方硬件不如發(fā)送方,或者系統繁忙、資源緊張時,是無法瞬間處理這么多報文的。于是,這些報文只能被丟掉,使得網絡效率非常低。

為了解決這種現象發(fā)生,TCP 提供一種機制可以讓「發(fā)送方」根據「接收方」的實際接收能力控制發(fā)送的數據量,這就是滑動窗口的由來。

接收方根據它的緩沖區(qū),可以計算出后續(xù)能夠接收多少字節(jié)的報文,這個數字叫做接收窗口。當內核接收到報文時,必須用緩沖區(qū)存放它們,這樣剩余緩沖區(qū)空間變小,接收窗口也就變小了;當進程調用 read 函數后,數據被讀入了用戶空間,內核緩沖區(qū)就被清空,這意味著主機可以接收更多的報文,接收窗口就會變大。

因此,接收窗口并不是恒定不變的,接收方會把當前可接收的大小放在 TCP 報文頭部中的窗口字段,這樣就可以起到窗口大小通知的作用。

發(fā)送方的窗口等價于接收方的窗口嗎?如果不考慮擁塞控制,發(fā)送方的窗口大小「約等于」接收方的窗口大小,因為窗口通知報文在網絡傳輸是存在時延的,所以是約等于的關系。

面試官:換人!他連 TCP 這幾個參數都不懂
TCP 頭部

從上圖中可以看到,窗口字段只有 2 個字節(jié),因此它最多能表達 65535 字節(jié)大小的窗口,也就是 64KB 大小。

這個窗口大小最大值,在當今高速網絡下,很明顯是不夠用的。所以后續(xù)有了擴充窗口的方法:在 TCP 選項字段定義了窗口擴大因子,用于擴大TCP通告窗口,使 TCP 的窗口大小從 2 個字節(jié)(16 位) 擴大為 30 位,所以此時窗口的最大值可以達到 1GB(2^30)。

Linux 中打開這一功能,需要把 tcp_window_scaling 配置設為 1(默認打開):

面試官:換人!他連 TCP 這幾個參數都不懂

要使用窗口擴大選項,通訊雙方必須在各自的 SYN 報文中發(fā)送這個選項:

  • 主動建立連接的一方在 SYN 報文中發(fā)送這個選項;

  • 而被動建立連接的一方只有在收到帶窗口擴大選項的 SYN 報文之后才能發(fā)送這個選項。

這樣看來,只要進程能及時地調用 read 函數讀取數據,并且接收緩沖區(qū)配置得足夠大,那么接收窗口就可以無限地放大,發(fā)送方也就無限地提升發(fā)送速度。

這是不可能的,因為網絡的傳輸能力是有限的,當發(fā)送方依據發(fā)送窗口,發(fā)送超過網絡處理能力的報文時,路由器會直接丟棄這些報文。因此,緩沖區(qū)的內存并不是越大越好。

如果確定最大傳輸速度?

在前面我們知道了 TCP 的傳輸速度,受制于發(fā)送窗口與接收窗口,以及網絡設備傳輸能力。其中,窗口大小由內核緩沖區(qū)大小決定。如果緩沖區(qū)與網絡傳輸能力匹配,那么緩沖區(qū)的利用率就達到了最大化。

問題來了,如何計算網絡的傳輸能力呢?

相信大家都知道網絡是有「帶寬」限制的,帶寬描述的是網絡傳輸能力,它與內核緩沖區(qū)的計量單位不同:

  • 帶寬是單位時間內的流量,表達是「速度」,比如常見的帶寬 100 MB/s;

  • 緩沖區(qū)單位是字節(jié),當網絡速度乘以時間才能得到字節(jié)數;

這里需要說一個概念,就是帶寬時延積,它決定網絡中飛行報文的大小,它的計算方式:

面試官:換人!他連 TCP 這幾個參數都不懂

比如最大帶寬是 100 MB/s,網絡時延(RTT)是 10ms 時,意味著客戶端到服務端的網絡一共可以存放 100MB/s * 0.01s = 1MB 的字節(jié)。

這個 1MB 是帶寬和時延的乘積,所以它就叫「帶寬時延積」(縮寫為 BDP,Bandwidth Delay Product)。同時,這 1MB 也表示「飛行中」的 TCP 報文大小,它們就在網絡線路、路由器等網絡設備上。如果飛行報文超過了 1 MB,就會導致網絡過載,容易丟包。

由于發(fā)送緩沖區(qū)大小決定了發(fā)送窗口的上限,而發(fā)送窗口又決定了「已發(fā)送未確認」的飛行報文的上限。因此,發(fā)送緩沖區(qū)不能超過「帶寬時延積」。

發(fā)送緩沖區(qū)與帶寬時延積的關系:

  • 如果發(fā)送緩沖區(qū)「超過」帶寬時延積,超出的部分就沒辦法有效的網絡傳輸,同時導致網絡過載,容易丟包;

  • 如果發(fā)送緩沖區(qū)「小于」帶寬時延積,就不能很好的發(fā)揮出網絡的傳輸效率。

所以,發(fā)送緩沖區(qū)的大小最好是往帶寬時延積靠近。

怎樣調整緩沖區(qū)大???

在 Linux 中發(fā)送緩沖區(qū)和接收緩沖都是可以用參數調節(jié)的。設置完后,Linux 會根據你設置的緩沖區(qū)進行動態(tài)調節(jié)

調節(jié)發(fā)送緩沖區(qū)范圍

先來看看發(fā)送緩沖區(qū),它的范圍通過 tcp_wmem 參數配置;

面試官:換人!他連 TCP 這幾個參數都不懂

上面三個數字單位都是字節(jié),它們分別表示:

  • 第一個數值是動態(tài)范圍的最小值,4096 byte = 4K;

  • 第二個數值是初始默認值,87380 byte ≈ 86K;

  • 第三個數值是動態(tài)范圍的最大值,4194304 byte = 4096K(4M);

發(fā)送緩沖區(qū)是自行調節(jié)的,當發(fā)送方發(fā)送的數據被確認后,并且沒有新的數據要發(fā)送,就會把發(fā)送緩沖區(qū)的內存釋放掉。

調節(jié)接收緩沖區(qū)范圍

而接收緩沖區(qū)的調整就比較復雜一些,先來看看設置接收緩沖區(qū)范圍的 tcp_rmem 參數:

面試官:換人!他連 TCP 這幾個參數都不懂

上面三個數字單位都是字節(jié),它們分別表示:

  • 第一個數值是動態(tài)范圍的最小值,表示即使在內存壓力下也可以保證的最小接收緩沖區(qū)大小,4096 byte = 4K;

  • 第二個數值是初始默認值,87380 byte ≈ 86K;

  • 第三個數值是動態(tài)范圍的最大值,6291456 byte = 6144K(6M);

接收緩沖區(qū)可以根據系統空閑內存的大小來調節(jié)接收窗口:

  • 如果系統的空閑內存很多,就可以自動把緩沖區(qū)增大一些,這樣傳給對方的接收窗口也會變大,因而提升發(fā)送方發(fā)送的傳輸數據數量;

  • 反正,如果系統的內存很緊張,就會減少緩沖區(qū),這雖然會降低傳輸效率,可以保證更多的并發(fā)連接正常工作;

發(fā)送緩沖區(qū)的調節(jié)功能是自動開啟的,而接收緩沖區(qū)則需要配置 tcp_moderate_rcvbuf 為 1 來開啟調節(jié)功能

面試官:換人!他連 TCP 這幾個參數都不懂

調節(jié) TCP 內存范圍

接收緩沖區(qū)調節(jié)時,怎么知道當前內存是否緊張或充分呢?這是通過 tcp_mem 配置完成的:

面試官:換人!他連 TCP 這幾個參數都不懂

上面三個數字單位不是字節(jié),而是「頁面大小」,1 頁表示 4KB,它們分別表示:

  • 當 TCP 內存小于第 1 個值時,不需要進行自動調節(jié);

  • 在第 1 和第 2 個值之間時,內核開始調節(jié)接收緩沖區(qū)的大??;

  • 大于第 3 個值時,內核不再為 TCP 分配新內存,此時新連接是無法建立的;

一般情況下這些值是在系統啟動時根據系統內存數量計算得到的。根據當前 tcp_mem 最大內存頁面數是 177120,當內存為 (177120 * 4) / 1024K ≈ 692M 時,系統將無法為新的 TCP 連接分配內存,即 TCP 連接將被拒絕。

根據實際場景調節(jié)的策略

在高并發(fā)服務器中,為了兼顧網速與大量的并發(fā)連接,我們應當保證緩沖區(qū)的動態(tài)調整的最大值達到帶寬時延積,而最小值保持默認的 4K 不變即可。而對于內存緊張的服務而言,調低默認值是提高并發(fā)的有效手段。

同時,如果這是網絡 IO 型服務器,那么,調大 tcp_mem 的上限可以讓 TCP 連接使用更多的系統內存,這有利于提升并發(fā)能力。需要注意的是,tcp_wmem 和 tcp_rmem 的單位是字節(jié),而 tcp_mem 的單位是頁面大小。而且,千萬不要在 socket 上直接設置 SO_SNDBUF 或者 SO_RCVBUF,這樣會關閉緩沖區(qū)的動態(tài)調整功能。

小結

本節(jié)針對 TCP 優(yōu)化數據傳輸的方式,做了一些介紹。

面試官:換人!他連 TCP 這幾個參數都不懂
數據傳輸的優(yōu)化策略

TCP 可靠性是通過 ACK 確認報文實現的,又依賴滑動窗口提升了發(fā)送速度也兼顧了接收方的處理能力。

可是,默認的滑動窗口最大值只有 64 KB,不滿足當今的高速網絡的要求,要想要想提升發(fā)送速度必須提升滑動窗口的上限,在 Linux 下是通過設置 tcp_window_scaling 為 1 做到的,此時最大值可高達 1GB。

滑動窗口定義了網絡中飛行報文的最大字節(jié)數,當它超過帶寬時延積時,網絡過載,就會發(fā)生丟包。而當它小于帶寬時延積時,就無法充分利用網絡帶寬。因此,滑動窗口的設置,必須參考帶寬時延積。

內核緩沖區(qū)決定了滑動窗口的上限,緩沖區(qū)可分為:發(fā)送緩沖區(qū) tcp_wmem 和接收緩沖區(qū) tcp_rmem。

Linux 會對緩沖區(qū)動態(tài)調節(jié),我們應該把緩沖區(qū)的上限設置為帶寬時延積。發(fā)送緩沖區(qū)的調節(jié)功能是自動打開的,而接收緩沖區(qū)需要把 tcp_moderate_rcvbuf 設置為 1 來開啟。其中,調節(jié)的依據是 TCP 內存范圍 tcp_mem。

但需要注意的是,如果程序中的 socket 設置 SO_SNDBUF 和 SO_RCVBUF,則會關閉緩沖區(qū)的動態(tài)整功能,所以不建議在程序設置它倆,而是交給內核自動調整比較好。

有效配置這些參數后,既能夠最大程度地保持并發(fā)性,也能讓資源充裕時連接傳輸速度達到最大值。


巨人的肩膀

[1] 系統性能調優(yōu)必知必會.陶輝.極客時間.

[2] 網絡編程實戰(zhàn)專欄.盛延敏.極客時間.

[3] http://www.blogjava.net/yongboy/archive/2013/04/11/397677.html

[4] http://blog.itpub.net/31559359/viewspace-2284113/

[5] https://blog.51cto.com/professor/1909022



特別推薦一個分享架構+算法的優(yōu)質內容,還沒關注的小伙伴,可以長按關注一下:

面試官:換人!他連 TCP 這幾個參數都不懂

長按訂閱更多精彩▼

面試官:換人!他連 TCP 這幾個參數都不懂

如有收獲,點個在看,誠摯感謝

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統 散熱

根據LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統,而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉