由于TCP連接本質上可以理解為是client-server端的一對socket內核對象,那么從理論上將應該是【2^32 (ip數) * 2^16 (端口數)】條連接(約等于兩百多萬億)
struct socket {
....
//INET域專用的一個socket表示, 提供了INET域專有的一些屬性,比如 IP地址,端口等
struct sock *sk;
//TCP連接的狀態:SYN_SENT、SYN_RECV、ESTABLISHED.....
short type;
....
};
struct inet_sock {
...
__u32 daddr; //IPv4的目標地址。
__u16 dport; //目標端口。
__u32 saddr; //源地址。
__u16 sport; //源端口。
...
};
這個 socket 對象也就是一個數據結構,里面包含了 TCP 四元組的信息:源IP、源端口、目標IP、目標端口。
如果只以ESTABLISH狀態的連接來算(這些連接只是建立,但是不收發數據也不處理相關的業務邏輯)那么一臺服務器最大能建立多少連接呢?
這種情況下,那么能建立的連接數量主要取決于【內存的大小】(因為如果是)ESTABLISH狀態的空閑連接,不會消耗CPU(雖然有TCP保活包傳輸,但這個影響非常小,可以忽略不計)
我們知道一條ESTABLISH狀態的連接大約消耗【3.3KB內存】,那么通過計算得知一臺4GB內存的服務器,【可以建立100w+的TCP連接】(當然這里只是計算所有的連接都只建立連接但不發送和處理數據的情況,如果真實場景中有數據往來和處理(數據接收和發送都需要申請內存,數據處理便需要CPU),那便會消耗更高的內存以及占用更多的CPU,并發不可能達到100w+)
上面討論的都是進建立連接的理想情況,在現實中如果有頻繁的數據收發和處理(比如:壓縮、加密等),那么一臺服務器能支撐1000連接都算好的了,所以一臺服務器能支撐多少連接還要結合具體的場景去分析,不能光靠理論值去算。拋開業務邏輯單純的談并發沒有太大的實際意義。
服務器的開銷大頭往往并不是連接本身,而是每條連接上的數據收發,以及請求業務邏輯處理!!!
三次握手里socket的全連接隊列長度由參數net.core.somaxconn來控制,默認大小是128,當兩臺機器離的非常近,但是建立連接的并發又非常高時,可能會導致半連接隊列或全連接隊列溢出,進而導致server端丟棄握手包。然后造成client超時重傳握手包(至少1s以后才會重傳),導致三次握手連接建立耗時過長。可以調整參數net.core.somaxconn來增加去按連接隊列的長度,進而減小丟包的影響在Linux一切皆文件,當然也包括之前TCP連接中說的socket。進程打開一個socket的時候需要創建好幾個內核對象,換一句直白的話說就是打開文件對象吃內存,所以Linux系統基于安全角度考慮(比如:有用戶進程惡意的打開無數的文件描述符,那不得把系統搞奔潰了),在多個位置都限制了可打開的文件描述符的數量。
內核是通過【hash表】的方式來管理所有已經建立好連接的socket,以便于有請求到達時快速的通過【TCP四元組】查找到內核中對應的socket對象
在epoll模型中,通過紅黑樹來管理epoll對象所管理的所有socket,用紅黑樹結構來平衡快速刪除、插入、查找socket的效率