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