最多能建立多少個 TCP 連線?

閃客sun發表於2021-07-01

我是一個 Linux 伺服器上的程式,名叫小進。

老是有人說我最多隻能建立 65535 個 TCP 連線。

我不信這個邪,今天我要親自去實踐一下。

我走到作業系統老大的跟前,說:

"老操,我要建立一個 TCP 連線!"

老操不慌不忙,拿出一個表格遞給我,"小進,先填表吧"

最多能建立多少個 TCP 連線?

我一看這個表,這不就是經典的 socket 四元組嘛。我只有一塊網路卡,其 IP 地址是 123.126.45.68,我想要與 110.242.68.3 的 80 埠建立一個 TCP 連線,我將這些資訊填寫在了表中。

最多能建立多少個 TCP 連線?

源埠號填什麼呢?我記得埠號是 16 位的,可以有 0 ~ 65535 這個範圍的數字,那我隨便選一個吧!

正當我猶豫到底選什麼數字的時候,老操一把搶過我的表格。

"你墨跡個啥呢小進?源埠號不用你填,我會給你分配一個可用的數字。源IP也不用你填,我知道都有哪些網路卡,並且會幫你選個合適的。真是個新手,回去等訊息吧。"

"哦"

老操帶著我的表格,走了。

過了很長時間,老操終於回來了,並且帶著一個紙條。

最多能建立多少個 TCP 連線?

"小進,你把這個收好了。"

我問道,"這是啥呀?"

老操不耐煩地說道,"剛剛說你是新手你還不服,這個 5 表示檔案描述符,linux 下一切皆檔案,你待會和你那個目標 IP 進行 TCP 通訊的時候,就對著這個檔案描述符讀寫就好啦。"

"這麼方便!好的,謝謝老操。"

我拿著這個檔案描述符,把它放到屬於我的記憶體中裱起來了,反正我只是想看看最多能建立多少 TCP 連線,又不是去真的用它,嘻嘻。

 

埠號

 

過了一分鐘,我又去找老操了。

"老操,我要建立一個 TCP 連線!"

老操不慌不忙,拿出一個表格遞給我,"小進,先填表吧"

最多能建立多少個 TCP 連線?

這回我熟悉了,只把目標IP和目標埠填好。

最多能建立多少個 TCP 連線?

老操辦好事之後,又帶著一個紙條回來,上面寫著數字"6"。

就這樣,我每隔一分鐘都去找老操建立一個新的 TCP 連線,目標 IP 都是110.242.68.3,目標埠都是 80。

老操也很奇怪,不知道我在這折騰啥,他雖然權力大,但無權拒絕我的指令,每次都兢兢業業地把事情辦好,並給我一張一張寫著檔案描述符的紙條。

直到有一次,我收到的紙條有些不同。

最多能建立多少個 TCP 連線?

我帶著些許責怪的語氣問,"老操,這是怎麼回事呀?"

老操也沒好氣地說,"這表示埠號不夠用啦!早就覺得你小子不對勁了,一個勁地對著同一個 IP 和埠建立 TCP 連線,之前沒辦法必須執行你給的指令,現在不行了,埠號不夠用了,源埠那裡我沒法給你填了。"

我也不是那麼好騙的,質疑道。"老操,你也別欺負我這個新手,我可是知道埠號是 16 位的,範圍是 1~65535,一共可以建立 65535 個 TCP 連線,我現在才建立了 63977 個,怎麼就不夠了!"

老操鄙視地看了我一眼,"你小子可真是閒的蛋疼啊,還真一個個數,來我告訴你吧,Linux 對可使用的埠範圍是有具體限制的,具體可以用如下命令檢視。"

[root]# cat /proc/sys/net/ipv4/ip_local_port_range 
1024 65000

"看到沒,當前的限制是1024~65000,所以你就只能有63977個埠號可以使用。"

最多能建立多少個 TCP 連線?

我趕緊像老操道歉,"哎喲真是抱歉,還是我見識太少,那這個數可以修改麼?"

老操也沒跟我一般見識,還是耐心地回答我,"可以的,具體可以 vim /etc/sysctl.conf 這個檔案進行修改,我們在這個檔案裡新增一行記錄"

net.ipv4.ip_local_port_range = 60000 60009

"儲存好後執行 sysctl -p /etc/sysctl.conf 使其生效。這樣你就只有 10 個埠號可以用了,就會更快報出埠號不夠用的錯誤"

"原來如此,謝謝老操又給我上了一課。"

哎不對,建立一個 TCP 連線,需要將通訊兩端的套接字(socket)進行繫結,如下:

源 IP 地址:源埠號 <---->  目標 IP 地址:目標埠號

只要這套繫結關係構成的四元組不重複即可,剛剛埠號不夠用了,是因為我一直對同一個目標IP和埠建立連線,那我換一個目標埠號試試。

最多能建立多少個 TCP 連線?
最多能建立多少個 TCP 連線?

看來成功了,只要源埠號不用夠用了,就不斷變換目標 IP 和目標埠號,保證四元組不重複,我就能建立好多好多 TCP 連線啦!

這也證明了有人說最多隻能建立 65535 個TCP連線是多麼荒唐。

 

檔案描述符

 

找到了突破埠號限制的辦法,我不斷找老操建立TCP連線,老操也拿我沒有辦法。

直到有一次,我又收到了一張特殊的紙條,上面寫的不是檔案描述符。

最多能建立多少個 TCP 連線?

我又沒好氣地問老操,"這又是咋回事?"

老操幸災樂禍地告訴我,"呵呵,你小子以為突破埠號限制就無法無天了?現在檔案描述符不夠用啦!"

"怎麼啥啥都有限制啊?你們作業系統給我們的限制也太多了吧?"

"廢話,你看看你都建了多少個TCP連線了!每建立一個TCP連線,我就得分配給你一個檔案描述符,linux 對可開啟的檔案描述符的數量分別作了三個方面的限制。"

系統級:當前系統可開啟的最大數量,通過 cat /proc/sys/fs/file-max 檢視

使用者級:指定使用者可開啟的最大數量,通過 cat /etc/security/limits.conf 檢視

程式級:單個程式可開啟的最大數量,通過 cat /proc/sys/fs/nr_open 檢視

最多能建立多少個 TCP 連線?

天呢,真是人在屋簷下呀,我趕緊看了看這些具體的限制。

[root ~]# cat /proc/sys/fs/file-max
100000
[root ~]# cat /proc/sys/fs/nr_open
100000
[root ~]# cat /etc/security/limits.conf
...
* soft nproc 100000
* hard nproc 100000

原來如此,我記得剛剛收到的最後一張紙條是。

最多能建立多少個 TCP 連線?

再之後就收到檔案描述符不夠的錯誤了。

我又請教老操,"老操,那這個限制可以修改麼?"

老操仍然耐心地告訴我,"當然可以,比如你想修改單個程式可開啟的最大檔案描述符限制為100,可以這樣。"

echo 100 > /proc/sys/fs/nr_open

"原來如此,我這就去把各種檔案描述符限制都改大一點,也不多,就在後面加個0吧"

"額,早知道不告訴你小子了。"老操再次用鄙視的眼睛看著我。

 

執行緒

 

突破了檔案描述符限制,我又開始肆無忌憚地建立起了TCP連線。

但我發現,老操的辦事效率越來越慢,建立一個TCP連線花的時間越來越久。

有一次,我忍不住責問老操,"你是不是在偷懶啊?之前找你建一個TCP連線就花不到一分鐘時間,你看看最近我哪次不是等一個多小時你才搞好?"

老操也忍不住了,"小進啊你還好意思說我,你知不知道你每建一個TCP連線都需要消耗一個執行緒來為你服務?現在我和CPU老大那裡都忙得不可開交了,一直在為你這好幾十萬個執行緒不停地進行上下文切換,我們精力有限啊,自然就沒法像以前那麼快為你服務了。"

最多能建立多少個 TCP 連線?

聽完老操的抱怨,我想起了之前似乎有人跟我說過 C10K 問題,就是當伺服器連線數達到 1 萬且每個連線都需要消耗一個執行緒資源時,作業系統就會不停地忙於執行緒的上下文切換,最終導致系統崩潰,這可不是鬧著玩的。

我趕緊像作業系統老大請教,"老操,實在不好意思,一直以為你強大無比,沒想到也有忙得不可開交的時候呀,那我們現在應該怎麼辦呀?"

老操無奈地說,"我勸你還是別再繼續玩了,沒什麼意義,不過我想你也不會聽我的,那我跟你說兩句吧。"

你現在這種每建一個TCP連線就建立一個執行緒的方式,是最傳統的多執行緒併發模型,早期的作業系統也只支援這種方式。但現在我進化了,我還支援 IO 多路複用的方式,簡單說就是一個執行緒可以管理多個 TCP 連線的資源,這樣你就可以用少量的執行緒來管理大量的 TCP 連線了。

最多能建立多少個 TCP 連線?

我一臉疑惑,"啥是 IO 多路複用啊?"。

老操一臉鄙視,"你這... 你去看看閃客的《你管這破玩意叫 IO 多路複用》,就明白了。"

這次真是大開眼界了,我趕緊把程式碼改成了這種 IO 多路複用的模型,將原來的 TCP 連線銷燬掉,改成同一個執行緒管理多個 TCP 連線,很快,作業系統老大就恢復了以往的辦事效率,同時我的 TCP 連線數又多了起來。

 

記憶體

 

突破了埠號、檔案描述符、執行緒數等重重限制的我,再次肆無忌憚地建立起了TCP連線。

直到有一次,我又收到了一張紅牌。

最多能建立多少個 TCP 連線?

嗨,又是啥東西限制了呀,改了不就完了。我不耐煩地問老操,"這回又是啥毛病?"

老操說道。"這個錯誤叫記憶體溢位,每個TCP連線本身,以及這個連線所用到的緩衝區,都是需要佔用一定記憶體的,現在記憶體已經被你佔滿了,不夠用了,所以報了這個錯。"

最多能建立多少個 TCP 連線?

我看這次老操特別耐心,也沒多說什麼,但想著被記憶體限制住了,有點不太開心,於是我讓老操幫我最後一個忙。

"老操呀,幫小進我最後一個忙吧,你權利大,你看看把那些特別佔記憶體的程式給殺掉,給我騰出點地方,我今天要完成我的夢想,看看TCP連線數到底能建立多少個!"

老操見我真的是夠拼的,便答應了我,殺死了好多程式,我很是感動。

 

CPU

 

有了老操為我爭取的記憶體資源,我又開始日以繼日地建立TCP連線。

老操也不再說什麼,同樣日以繼日地執行著我的指令。

有一次,老操語重心長地對我說,"差不多了,我勸你就此收手吧,現在 CPU 的佔用率已經快到 100% 了。"

最多能建立多少個 TCP 連線?

我覺得老操這人真的可笑,經過這幾次的小挫折,我明白了只要思想不滑坡,方法總比苦難多,老操這人就是太謹慎了,我豈能半途而廢,不管他。

我仍然繼續建立著 TCP 連線。

直到有一天,老操把我請到一個小飯館,一塊吃了頓飯,吃好後說道。"我們哥倆也算是配合了很久啦,今天我是來跟你道個別的。"

我很不解地問,"怎麼了老操,發生什麼事了?。"

老操說,"由於你的 TCP 連線,CPU 佔用率已經很長時間維持在 100%,我們的使用者,也就是我們的上帝,幾乎什麼事情都做不了了,連滑鼠動一下都要等好久,所以他給我下達了一個重啟的指令,我執行這個指令後,你,以及像你一樣的所有程式,包括我這個作業系統本身,一切都就消失了。"

我大驚失色,"啊,這麼突然麼?這條指令什麼時候執行?"

老操緩緩起身,"就現在了,剛剛這條指令還沒得到 CPU 執行的機會,不過現在到了。"

突然,我眼前一黑,一切都沒了。

 

總結

 

最多能建立多少個 TCP 連線?

資源

一臺Linux伺服器的資源

一個TCP連線佔用的資源

佔滿了會發生什麼

CPU

看你花多少錢買的

看你用它幹嘛

電腦卡死

記憶體

看你花多少錢買的

取決於緩衝區大小

OOM

臨時埠號

ip_local_port_range

1

cannot assign requested address

檔案描述符

fs.file-max

1

too many open files

程式\執行緒數

ulimit -n

看IO模型

系統崩潰

 


後記


其實這個問題,我覺得結論不重要,最重要的是思考過程。

而思考過程其實相當簡單,就是,尋找限制條件而已,其實一開始這篇文章,我寫了個故事在開頭,但後來感覺放在後記更合適。故事是這樣的。

閃客:小宇,我問你,你一天最多能吃多少個漢堡?

小宇:額,你這問的太隱私了吧,不過看在你教我技術的份上,我就告訴你,最多能吃 4 個左右吧。

閃客:咳咳真的麼?好吧,那你一分鐘最多能吃多少個漢堡?

小宇:快的話可能 2 個,不過正常應該最多就能吃完 1 個了。

閃客:好的,那我問你,剛剛這兩個問題你為什麼能不假思索地回答出來呢?

小宇:哈哈你這是什麼話,我自己我當然瞭解了。

閃客:不,你仔細想想你回答這兩個問題的邏輯。

小宇:哦我明白你的意思了,當你問我一天最多能吃多少個漢堡時,我考慮的是我的胃的容量最多能容下多少個漢堡。而當你問我一分鐘最多能吃多少個漢堡時,我考慮的時我吃漢堡的速度,按照這個速度在一分鐘內能吃多少。

閃客:沒錯,你總結得很好!一天最多吃多少個漢堡,此時時間非常充裕,所以主要是胃的容量限制了這個漢堡最大值,計算公式應該是:

最多漢堡數 = 胃的容量 ÷ 漢堡的體積

最多能建立多少個 TCP 連線?

而一分鐘最多吃多少個漢堡,此時胃的容量非常充裕,限制漢堡最大值的是時間因素,計算公式是:

最多漢堡數 = 一分鐘 ÷ 吃一個漢堡的耗時

最多能建立多少個 TCP 連線?

所以,取決於最先觸達的那個限制條件。

而最大 TCP 連線數這個問題,假如面試被問到了,即使你完全不會,也應該有這樣的思路。

而如果你有了這樣的思路,你多多少少都能回答出讓面試官滿意的答案,因為計算機很多時候,更看重思路,而不是細枝末節。

 

應該有這樣的思路。

而如果你有了這樣的思路,你多多少少都能回答出讓面試官滿意的答案,因為計算機很多時候,更看重思路,而不是細枝末節。

 

相關文章