新聞中心
什么是TCP并發(fā)

TCP并發(fā)是指一個服務器同時“hold住”的連接數(shù)量,確切的說就是指服務器端看到的“ESTABLISHED”狀態(tài)的TCP連接數(shù)量。通過netstat -n|grep ^tcp|awk '{print $NF}'|sort -nr|uniq -c可以查看當前服務器TCP狀態(tài)統(tǒng)計報告,下圖是我的執(zhí)行結(jié)果(我正在通過SSH連接這臺機器所以有一個“ESTABLISHED”狀態(tài)的TCP連接)
測試TCP并發(fā)就是指讓這個值達到的頂峰,要實現(xiàn)這個必須滿足兩點:
- 短時間內(nèi)構造百萬級連接
- 服務器端同時hold住百萬級連接
需要注意的是上面的“測試”不包括“連接之后的交互”僅僅是指“hold住連接”。
傳統(tǒng)工具為什么無法滿足
很多服務器都是TCP結(jié)構的比如Mysql、Tomcat、Nginx,這些工具也有相應的壓力測試工具,比較著名的包括:Jmeter、Tsung。這些工具的實現(xiàn)基本上是一致的
- 同時啟動多個任務
- 每個任務打開一個socket連接到服務器
這種測試方法受限于三個資源
- 可以啟動的任務數(shù)量(線程數(shù)或者進程數(shù))
- 可以打開的socket數(shù)量(文件描述符)
- 受限于本機可用端口最大值——65535
第一個限制我們可以通過“協(xié)程”之類的技術手段解決;第二個限制在內(nèi)存滿足的情況下可以通過調(diào)整系統(tǒng)參數(shù)解決(參考我的《你真知道“Too many open files”?》);第三個限制幾乎是致命的——傳統(tǒng)上只能通過多臺服務器一塊協(xié)同。
即便解決了上述三個問題也很難在“短時間”內(nèi)造成巨大的壓力,大量的socket會吃光內(nèi)存,多臺服務器協(xié)同必然是一個分布式問題(想想就掉頭發(fā))。
新的思路
TCP連接給人的感覺是一個“通道”,這其實這是一個“錯覺”。所有的網(wǎng)絡基本上都是基于“存儲轉(zhuǎn)發(fā)”的經(jīng)過。三次握手之后的TCP連接到達“ESTABLISHED”狀態(tài),服務器會為它保留資源——即使客戶端已經(jīng)不再理睬這個連接。那么我們是不是可以不經(jīng)過TCP/IP協(xié)議棧直接通過raw socket構造三次握手呢?只要我們大批量的構造三次握手就可以對服務器構成巨大的壓力了。
我們重點關注Client->Server的兩個箭頭。第一個數(shù)據(jù)包是SYN數(shù)據(jù)包,seq=隨機數(shù);第二個數(shù)據(jù)包是ACK數(shù)據(jù)包,ACK=收到數(shù)據(jù)包的seq+1,seq=收到數(shù)據(jù)包的ack。其實TCP數(shù)據(jù)包之間是沒有直接關系的,我們收到一個數(shù)據(jù)包就可以直接算出回復數(shù)據(jù)包的ack、seq
上面的思路基本上可以證明我們的方法在理論上是可行的,在實踐上我們還需要克服一些問題
- 怎么獲取Server到Client的SYN+ACK(三次握手中的第二個箭頭);畢竟我們不是直接使用Socket打開TCP連接(這樣做就不需要自己構造TCP三次握手了)
- Server在收到Client請求后會嘗試對Client進行ARP地址,如果發(fā)現(xiàn)無法解析就認為是一個非法的數(shù)據(jù)包直接發(fā)送RST數(shù)據(jù)包關閉TCP連接
第一個問題我們通過libpcap“旁路”kernel,直接獲取原始數(shù)據(jù)包。下圖是libpcap的原理
libpcap底層使用的是BPF(Berkeley Packet Filter)驅(qū)動,這是kernel中專門用來“調(diào)試”的驅(qū)動程序最早是為Unix開發(fā)現(xiàn)在已經(jīng)成為各種操作系統(tǒng)的標配(只要支持tcpdump那么底層一定是有實現(xiàn)這個驅(qū)動模型的)。它獨立于kernel中的其他協(xié)議棧直接和讀取數(shù)據(jù)鏈路層的數(shù)據(jù)包。
通過libpcap我們可以獲取所有的數(shù)據(jù)包(即便操作系統(tǒng)不處理)然后構造自己的數(shù)據(jù)包通過raw socket直接把寫入到數(shù)據(jù)鏈路層。整個“收包”->“處理”->“回包”完全不需要kernel參與。
第二個問題其實在前面的文章中我已經(jīng)給出了答案——構造并且回復ARP數(shù)據(jù)包(《深入理解ARP攻擊 》)。簡單來說就是通過libpcap獲取arp request,通過raw socket回復arp response。
動手
我努力去掉所有不相關的東西只保留了最精簡的部分,不到300行的代碼。代碼分為兩大部分“發(fā)起TCP SYN數(shù)據(jù)包”和“回復SYN+ACK數(shù)據(jù)包、ARP request數(shù)據(jù)包”。具體內(nèi)容可以看這里
這次我特意放上了cmake文件,執(zhí)行以下cmake就可以編譯了。
https://github.com/fireflyc/million-tcp-client
模擬多個IP地址
受限于端口上限,一臺服務器只能模擬65535個TCP連接。但是我提供的演示程序是可以指定IP地址的,這個IP地址只需要和目標IP在同一個網(wǎng)絡內(nèi)就可以了。比如我的測試環(huán)境:
測試機器有自己的IP地址172.16.46.128,但是這個IP地址并沒有用途,只是為了方便我SSH連接。服務器的IP地址是172.16.46.133,我啟動三個tcp-client分別綁定200、201、202。
每個TCP-Client進程都可以模擬65535個TCP連接。(這個其實還有改進的余地)
【本文是專欄作者邢森的原創(chuàng)文章,轉(zhuǎn)載請聯(lián)系作者本人獲取授權】
分享文章:模擬百萬級TCP并發(fā)
分享路徑:http://m.fisionsoft.com.cn/article/dhposoc.html


咨詢
建站咨詢
