1. 程式人生 > >關於 TCP 併發連線的幾個思考題與試驗

關於 TCP 併發連線的幾個思考題與試驗

陳碩 (giantchen AT gmail)

blog.csdn.net/Solstice

第一道初級題目是:

有一臺機器,它有一個 IP,上面運行了一個 TCP 服務程式,程式只偵聽一個埠,問:從理論上講(只考慮 TCP/IP 這一層面,不考慮IPv6)這個服務程式可以支援多少併發 TCP 連線?答 65536 上下的直接刷掉。

具體來說,這個問題等價於:有一個 TCP 服務程式的地址是 1.2.3.4:8765,問它從理論上能接受多少個併發連線?

第二道進階題目是:

一臺被測機器 A,功能同上,同一交換機上還接有一臺機器 B,如果允許 B 的程式直接收發乙太網 frame,問:讓 A 承擔 10 萬個併發 TCP 連線需要用多少 B 的資源?100萬個呢?

從討論的結果看,很多人做出了第一道題,而第二道題幾乎無人問津。

這裡先不公佈答案(第一題答案見文末),讓我們繼續思考一個本質的問題:一個 TCP 連線要佔用多少系統資源。

在現在的 Linux 作業系統上,如果用 socket()/connect() 或 accept() 來建立 TCP 連線,那麼每個連線至少要佔用一個檔案描述符(file descriptor)。為什麼說“至少”?因為檔案描述符可以複製,比如 dup();也可以被繼承,比如 fork();這樣可能出現系統裡邊同一個 TCP 連線有多個檔案描述符與之對應。據此,很多人給出的第一題答案是:併發連線數受限於系統能同時開啟的檔案數目的最大值。這個答案在實踐中是正確的,卻不符合原題意。

如果拋開作業系統層面,只考慮 TCP/IP 層面,建立一個 TCP 連線有哪些開銷?理論上最小的開銷是多少?考慮兩個場景:

1. 假設有一個 TCP 服務程式,向這個程式成功發起連線需要做哪些事情?換句話說,如何才能讓這個 TCP 服務程式認為有客戶連線到了它(讓它的 accept() 呼叫正常返回)?

2. 假設有一個 TCP 客戶端程式,讓這個程式成功建立到伺服器的連線需要做哪些事情?換句話說,如何才能讓這個 TCP 客戶端程式認為它自己已經連線到伺服器了(讓它的 connect() 呼叫正常返回)?

以上這兩個問題問的不是如何程式設計,如何呼叫 Sockets API,而是問如何讓作業系統的 TCP/IP 協議棧認為任務已經成功完成,連線已經成功建立。

學過 TCP/IP 協議,理解三路握手的同學明白,TCP 連線是虛擬的連線,不是電路連線,維持 TCP 連線理論上不佔用網路資源(會佔用兩頭程式的系統資源)。只要連線的雙方認為 TCP 連線存在,並且可以互相傳送 IP packet,那麼 TCP 連線就一直存在。

對於問題 1,向一個 TCP 服務程式發起一個連線,客戶端(為明白起見,以下稱為 faketcp 客戶端)只需要做三件事情(三路握手):

1a. 向 TCP 服務程式發一個 IP packet,包含 SYN 的 TCP segment

1b. 等待對方返回一個包含 SYN 和 ACK 的 TCP segment

1c. 向對方傳送一個包含 ACK 的 segment

在做完這三件事情之後,TCP 伺服器程式會認為連線已建立。而做這三件事情並不佔用客戶端的資源(?),如果faketcp 客戶端程式可以繞開作業系統的 TCP/IP 協議棧,自己直接傳送並接收 IP packet 或 Ethernet frame 的話。換句話說,faketcp 客戶端可以一直重複做這三件事件,每次用一個不同的 IP:PORT,在服務端建立不計其數的 TCP 連線,而 faketcp 客戶端自己毫髮無損。很快我們將看到如何用程式來實現這一點。

對於問題 2,為了讓一個 TCP 客戶端程式認為連線已建立,faketcp 服務端只需要做兩件事情:

2a. 等待客戶端發來的 SYN TCP segment

2b. 傳送一個包含 SYN 和 ACK 的 TCP segment

2c. 忽視對方發來的包含 ACK 的 segment

在做完這兩件事情(收一個 SYN、發一個 SYN+ACK)之後,TCP 客戶端程式會認為連線已建立。而做這三件事情並不佔用 faketcp 服務端的資源(?)換句話說,faketcp 服務端可以一直重複做這兩件事件,接受不計其數的 TCP 連線,而 faketcp 服務端自己毫髮無損。很快我們將看到如何用程式來實現這一點。

基於對以上兩個問題的分析,說明單獨談論“TCP 併發連線數”是沒有意義的,因為連線數基本上是要多少有多少。更有意義的效能指標或許是:“每秒鐘收發多少條訊息”、“每秒鐘收發多少位元組的資料”、“支援多少個活動的併發客戶”等等。

faketcp 的程式實現

為了驗證我上面的說法,我寫了幾個小程式來實現 faketcp,這幾個程式可以發起或接受不計其數的 TCP 併發連線,並且不消耗作業系統資源,連動態記憶體分配都不會用到。

我家裡有一臺執行 Ubuntu Linux 10.04 的 PC 機,hostname 是 atom,所有的試驗都在這上面進行。

家裡試驗環境的網路配置是:

net

陳碩在《談一談網路程式設計學習經驗》中曾提到“可以用 TUN/TAP 裝置在使用者態實現一個能與本機點對點通訊的 TCP/IP 協議棧”,這次的試驗正好可以用上這個辦法。

試驗的網路配置是:

tun

具體做法是:在 atom 上通過開啟 /dev/net/tun 裝置來建立一個 tun0 虛擬網絡卡,然後把這個網絡卡的地址設為 192.168.0.1/24,這樣 faketcp 程式就扮演了 192.168.0.0/24 這個網段上的所有機器。atom 發給 192.168.0.2~192.168.0.254 的 IP packet 都會發給 faketcp 程式,faketcp 程式可以模擬其中任何一個 IP 給 atom 發 IP packet。

程式分成幾步來實現。

第一步:實現 icmp echo 協議,這樣就能 ping 通 faketcp 了。

執行方法,開啟 3 個命令列視窗:

1. 在第 1 個視窗執行 sudo ./icmpecho ,程式顯示

allocted tunnel interface tun0

2. 在第 2 個視窗執行

$ sudo ifconfig tun0 192.168.0.1/24

$ sudo tcpdump -i tun0

3. 在第 3 個視窗執行

$ ping 192.168.0.2

$ ping 192.168.0.3

$ ping 192.168.0.234

發現每個 192.168.0.X 的 IP 都能 ping 通。

第二步:實現拒絕 TCP 連線的功能,即在收到 SYN TCP segment 的時候傳送 RST segment。

執行方法,開啟 3 個命令列視窗,頭兩個視窗的操作與前面相同,執行的 faketcp 程式是 ./rejectall

3. 在第 3 個視窗執行

$ nc 192.168.0.2 2000

$ nc 192.168.0.2 3333

$ nc 192.168.0.7 5555

發現向其中任意一個 IP 發起的 TCP 連線都被拒接了。

第三步:實現接受 TCP 連線的功能,即在收到SYN TCP segment 的時候發回 SYN+ACK。這個程式同時處理了連線斷開的情況,即在收到 FIN segment 的時候發回 FIN+ACK。

執行方法,開啟 3 個命令列視窗,步驟與前面相同,執行的 faketcp 程式是 ./acceptall。這次會發現 nc 能和 192.168.0.X 中的每一個 IP 每一個 PORT 都能連通。還可以在第 4 個視窗中執行 netstat –tpn ,以確認連線確實建立起來了。如果在 nc 中輸入資料,資料會堆積在作業系統中,表現為 netstat 顯示的傳送佇列(Send-Q)的長度增加。

第四步:在第三步接受 TCP 連線的基礎上,實現接收資料,即在收到包含 payload 資料 的 TCP segment 時發回 ACK。

執行方法,開啟 3 個命令列視窗,步驟與前面相同,執行的 faketcp 程式是 ./acceptall。這次會發現 nc 能和 192.168.0.X 中的每一個 IP 每一個 PORT 都能連通,資料也能發出去。還可以在第 4 個視窗中執行 netstat –tpn ,以確認連線確實建立起來了,並且傳送佇列的長度為 0。

這一步已經解決了前面的問題 2,扮演任意 TCP 服務端。

第五步:解決前面的問題 1,扮演客戶端向 atom 發起任意多的連線。

這一步的執行方法與前面不同,開啟 4 個命令列視窗。

1. 在第 1 個視窗執行 sudo ./connectmany 192.168.0.1 2007 1000 ,表示將向 192.168.0.1:2007 發起 1000 個併發連線。

程式顯示

allocted tunnel interface tun0
press enter key to start connecting 192.168.0.1:2007

2. 在第 2 個視窗執行

$ sudo ifconfig tun0 192.168.0.1/24

$ sudo tcpdump -i tun0

3. 在第 3 個視窗執行一個能接收併發 TCP 連線的服務程式,可以是 httpd,也可以是 muduo 的 echo 或 discard 示例,程式應 listen 2007 埠。

4. 回到第 1 個視窗中敲回車,然後在第 4 個視窗中用 netstat -tpn 來觀察併發連線。

有興趣的話,還可以繼續擴充套件,做更多的有關 TCP 的試驗,以進一步加深理解,驗證作業系統 TCP/IP 協議棧面對不同輸入的行為。甚至可以按我在《談一談網路程式設計學習經驗》中提議的那樣,實現完整的 TCP 狀態機,做出一個簡單的 mini tcp stack。

第一道題的答案:

在只考慮 IPv4 的情況下,併發數的理論上限是 2**48。考慮某些 IP 段被保留了,這個上界可適當縮小,但數量級不變。實際的限制是作業系統全域性檔案描述符的數量,以及記憶體大小。

一個 TCP 連線有兩個 end points,每個 end point 是 {ip, port},題目說其中一個 end point 已經固定,那麼留下一個 end point 的自由度,即 2 ** 48。客戶端 IP 的上限是 2**32 個,每個客戶端IP發起連線的上限是 2**16,乘到一起得理論上限。

即便客戶端使用 NAT,也不影響這個理論上限。(為什麼?)

在真實的 Linux 系統中,可以通過調整核心引數來支援上百萬併發連線,具體做法見:

(.完.)

相關推薦

關於 TCP 併發連線思考題試驗

陳碩 (giantchen AT gmail) blog.csdn.net/Solstice 第一道初級題目是: 有一臺機器,它有一個 IP,上面運行了一個 TCP 服務程式,程式只偵聽一個埠,問:從理論上講(只考慮 TCP/IP 這一層面,不考慮IPv6)這個服務程式可以

關於 TCP 併發連線思考題試驗GOOD

陳碩 (giantchen AT gmail) blog.csdn.net/Solstice 第一道初級題目是: 有一臺機器,它有一個 IP,上面運行了一個 TCP 服務程式,程式只偵聽一個埠,問:從理論上講(只考慮 TCP/IP 這一層面,不考慮IPv6)這個服務程式可以支援多少併發 TCP 連線?答

Java 多執行緒高併發 1 — 重要的概念

原子性: 和物理上的原子貌似沒多大關係,對於一系列的操作只能單個執行緒做處理,不能有其他執行緒干擾,例如多個人想往同一個杯子倒茶,但只有一個茶壺,只有等這個茶壺讓出來才能繼續讓其他人倒茶 有序性:

Cacti監控Tcp併發連線

轉自:http://blog.sina.com.cn/s/blog_5093fd5001015kw4.html 我的環境是 Version 0.8.7i 步驟一 從http://forums.cacti.net/download/file.php?id=5198下

xmlreaderxmlwriter裡的解決方案

  載入超過100M的xml檔案時(可能不是很常見),XmlDocument這種全部載入到記憶體裡的模式就有點不友好了,耗時長、記憶體高。 這時用xmlreader就會有自行車換超跑的感覺,但其間遇到幾個坑,記錄一下。 先看原始碼,包括dom和sax兩種模式的讀取和寫入 DOM模式:

xmlreaderxmlwriter裏的解決方案

mat tst 讀數 遇到的問題 fault tin family simple 1.0 加載超過100M的xml文件時(可能不是很常見),XmlDocument這種全部加載到內存裏的模式就有點不友好了,耗時長、內存高。 這時用xmlreader就會有自行車換超跑的感覺

Java中有關多執行緒併發工具類之總結

概述 在JDK的併發包(java.util.concurrent)中,為我們提供了幾個非常重要的併發工具類,分別為CountDownLatch、CyclicBarrier、Semaphore和Exchanger,下面將一一對這幾個類的功能及基本用法做詳細講解。 1)Cou

ThreadPoolExecutor 疑惑解答

keepal cor throw 線程數 核心 判斷 並且 on() 阻塞 任務是否都要先放入隊列? 當工作線程數小於核心線程數時,任務是不會經過隊列,而是直接創建 Worker 時傳入。但是如果工作線程數已經大於核心線程數,則任務是要先放入隊列的。實際上只要是被創建的工

Tcp連線連線對高併發的影響

1、Tcp建立連線需要三次握手,斷開連線需要四次握手。 2、tcp建立連線後將會在作業系統核心記憶體中維護四元組物件(源ip,源port,目標ip,目標port)。 3、tcp在斷開連結時,主動斷開方四元組將會處於time_wait狀態,同時源port不可用,此由作業系統限

jQuery中的$(window)$(document)用法區別

圖片 document window function 準備就緒 [window對象] 它是一個頂層對象,而不是另一個對象的屬性,即表示瀏覽器中打開的窗口。 1、屬性  defaultStatus 缺省的狀態條消息  document 當前顯示的文檔(該屬性本身也是一個對象)  f

那本叫編程珠璣的書中數學相關的問題

珠璣 其中 bsp ram 正整數 又是 問題 競賽題 Language 。。。又是一篇裝逼水文,先挖坑。。。 會寫一下證明,當然了都是小學生競賽題。。。到泰勒級數就封頂了。。。 1. 連續數組最大和的分治算法的時間復雜度為O(nlogn) 2. 證明72法則,科學炒

URL處理關鍵的函數parse_url、parse_strhttp_build_query

size print htm pos url 包含 targe encode 下標 1.parse_url() 該函數可以解析 URL,返回其組成部分。它的用法如下: array parse_url(string $url) 此函數返回一個關聯數組,包含現有 URL 的各種

Git(二)Git區的關系Git和GitHub的關聯

備份 gpo 技術分享 tin 2.3 div 自動 泄露 blog 前言   前面只是大概的介紹了一點基礎的東西,接下來會更加深入的去了解一下Git。 一、Git的工作區、暫存區和版本庫之間的區別和聯系   1)工作區     在PC中能看得到的創建的一個管理倉庫

《SQL Server企業級平臺管理實踐》讀書筆記——系統庫的備份恢復

工具 損壞 不能 數據庫比較 註釋 gen express 其它 ros master數據庫 master作為數據庫的主要數據庫,記錄著SQL Server系統的所有系統級信息,例如登錄用戶、系統配置設置、端點和憑證以及訪問其他數據服務器所需要的信息。master數據庫還

計量經濟時間序列_時間序列分析的基本概念(自相關函數,偏自相關函數等)

sig 永不 均值 blog 那種 屬於 class 觀察 自相關 1. 在時間序列分析中, 數學模型是什麽?數學公式又是什麽?數學推導過程又是什麽?... ...   一句話:用數學公式後者符號來表示現實存在的意義。數學是“萬金油”的科學,它是作為工作和分析方法運用到某

第五次作業——python效能分析問題(個人作業)

結合 撰寫 porting tin 設計實現 cti personal 設計文檔 hub 第五次作業——效能分析與幾個問題(個人作業) 前言 閱讀了大家對於本課程的目標和規劃之後,想必很多同學都躍躍欲試,迫不及待想要提高自身實踐能力,那麽就從第一個個人項目開始吧,題目要求見

第五節:SignalR大雜燴(MVC融合、全局的配置、跨域的應用、C/S程序充當Client和Server)

融合 變化 方法 signalr 以管理員身份運行 cal 大小 整合 ref 一. 說在前面的話   本節主要在前面章節的基礎上補充了幾個簡單的知識點,比如:第三方調用通過 GlobalHost.ConnectionManager.GetHubContext<

hadoop--Hadoop生態上技術的關系區別:hive、pig、hbase 關系區別

正是 讀寫操作 java 結構化 映射 map 外部表 base metastore Pig 一種操作hadoop的輕量級腳本語言,最初又雅虎公司推出,不過現在正在走下坡路了。當初雅虎自己慢慢退出pig的維護之後將它開源貢獻到開源社區由所有愛好者來維護。不過現在還是有些公司

一個正定矩陣 可以寫成它的特徵值正定矩陣的乘積和

最近看 Byod 的凸優化書,裡面有這個表示式,若 X X X 為正定矩陣,則

非遞迴棧式回溯遞迴法的問題

1.迷宮求解 2.八皇后 非遞迴 #include <stdio.h> #include <math.h> #include <malloc.h> void nQueens(int *x, int n); /*求解n皇后問題*/ int place