TCP accept返回的socket會在服務端新開一個端口嘛?服務端TCP連接數限制

原文鏈接:https://blog.csdn.net/hik_zxw/article/details/46438081

as you know,一個socket是由一個五元組來唯一標示的,即(協議,server_ip, server_port, client_ip, client_port)。只要該五元組中任何一個值不同,則其代表的socket就不同。這裏忽略協議的區別,在同一協議的基礎上,服務器端的listen socket的端口可以看成(server_ip, server_port, ***, ***),其中***是通配符,它跟任何一個client_ip, client_port值都不同,可以簡單看成是(0,0)對,當然實現不是這樣的。這樣在服務器端accept之後,返回的連接socket的四元組就是(server_ip, server_port, client_ip, client_port),這裏的client_ip,client_port因連接的客戶端的不同而不同。所以accept返回的socket和listen socket是不同的,不同之處就在於四元組中的客戶端ip和port,而服務器端的server_ip和server_port還是相同的,也就是accpet()函數返回的新的socket描述符的端口和listen端口是一樣的。可以使用getsockname()函數來查看它們之間的不同。

 

http://blog.csdn.net/hanzengyi/article/details/5365029

 

accept是又產生一個Socket端口嗎?

      要寫網絡程序就必須用Socket,這是程序員都知道的。而且,面試的時候,我們也會問對方會不會Socket編程?一般來說,很多人都會說,Socket編程基本就是listen,accept以及send,write等幾個基本的操作。是的,就跟常見的文件操作一樣,只要寫過就一定知道。

 

      對於網絡編程,我們也言必稱TCP/IP,似乎其它網絡協議已經不存在了。對於TCP/IP,我們還知道TCP和UDP,前者可以保證數據的正確和可靠性,後者則允許數據丟失。最後,我們還知道,在建立連接前,必須知道對方的IP地址和端口號。除此,普通的程序員就不會知道太多了,很多時候這些知識已經夠用了。最多,寫服務程序的時候,會使用多線程來處理併發訪問。

 

我們還知道如下幾個事實:

     1.一個指定的端口號不能被多個程序共用。比如,如果IIS佔用了80端口,那麼Apache就不能也用80端口了。

     2.很多防火牆只允許特定目標端口的數據包通過。

     3.服務程序在listen某個端口並accept某個連接請求後,會生成一個新的socket來對該請求進行處理。

 

     於是,一個困惑了我很久的問題就產生了。如果一個socket創建後並與80端口綁定後,是否就意味着該socket佔用了80端口呢?如果是這樣的,那麼當其accept一個請求後,生成的新的socket到底使用的是什麼端口呢(我一直以爲系統會默認給其分配一個空閒的端口號)?如果是一個空閒的端口,那一定不是80端口了,於是以後的TCP數據包的目標端口就不是80了--防火牆一定會阻止其通過的!實際上,我們可以看到,防火牆並沒有阻止這樣的連接,而且這是最常見的連接請求和處理方式。我的不解就是,爲什麼防火牆沒有阻止這樣的連接?它是如何判定那條連接是因爲connet80端口而生成的?是不是TCP數據包裏有什麼特別的標誌?或者防火牆記住了什麼東西?

 

      後來,我又仔細研讀了TCP/IP的協議棧的原理,對很多概念有了更深刻的認識。比如,在TCP和UDP同屬於傳輸層,共同架設在IP層(網絡層)之上。而IP層主要負責的是在節點之間(End to End)的數據包傳送,這裏的節點是一臺網絡設備,比如計算機。因爲IP層只負責把數據送到節點,而不能區分上面的不同應用,所以TCP和UDP協議在其基礎上加入了端口的信息,端口於是標識的是一個節點上的一個應用。除了增加端口信息,UPD協議基本就沒有對IP層的數據進行任何的處理了。而TCP協議還加入了更加複雜的傳輸控制,比如滑動的數據發送窗口(Slice Window),以及接收確認和重發機制,以達到數據的可靠傳送。不管應用層看到的是怎樣一個穩定的TCP數據流,下面傳送的都是一個個的IP數據包,需要由TCP協議來進行數據重組。

 

      所以,我有理由懷疑,防火牆並沒有足夠的信息判斷TCP數據包的更多信息,除了IP地址和端口號。而且,我們也看到,所謂的端口,是爲了區分不同的應用的,以在不同的IP包來到的時候能夠正確轉發。

 

     TCP/IP只是一個協議棧,就像操作系統的運行機制一樣,必須要具體實現,同時還要提供對外的操作接口。就像操作系統會提供標準的編程接口,比如Win32編程接口一樣,TCP/IP也必須對外提供編程接口,這就是Socket編程接口--原來是這麼回事啊!

 

在Socket編程接口裏,設計者提出了一個很重要的概念,那就是socket。這個socket跟文件句柄很相似,實際上在BSD系統裏就是跟文件句柄一樣存放在一樣的進程句柄表裏。這個socket其實是一個序號,表示其在句柄表中的位置。這一點,我們已經見過很多了,比如文件句柄,窗口句柄等等。這些句柄,其實是代表了系統中的某些特定的對象,用於在各種函數中作爲參數傳入,以對特定的對象進行操作--這其實是C語言的問題,在C++語言裏,這個句柄其實就是this指針,實際就是對象指針啦。

 

現在我們知道,socket跟TCP/IP並沒有必然的聯繫。Socket編程接口在設計的時候,就希望也能適應其他的網絡協議。所以,socket的出現只是可以更方便的使用TCP/IP協議棧而已,其對TCP/IP進行了抽象,形成了幾個最基本的函數接口。比如create,listen,accept,connect,read和write等等。

 

現在我們明白,如果一個程序創建了一個socket,並讓其監聽80端口,其實是向TCP/IP協議棧聲明瞭其對80端口的佔有。以後,所有目標是80端口的TCP數據包都會轉發給該程序(這裏的程序,因爲使用的是Socket編程接口,所以首先由Socket層來處理)。所謂accept函數,其實抽象的是TCP的連接建立過程。accept函數返回的新socket其實指代的是本次創建的連接,而一個連接是包括兩部分信息的,一個是源IP和源端口,另一個是宿IP和宿端口。所以,accept可以產生多個不同的socket,而這些socket裏包含的宿IP和宿端口是不變的,變化的只是源IP和源端口。這樣的話,這些socket宿端口就可以都是80,而Socket層還是能根據源/宿對來準確地分辨出IP包和socket的歸屬關係,從而完成對TCP/IP協議的操作封裝!而同時,放火牆的對IP包的處理規則也是清晰明瞭,不存在前面設想的種種複雜的情形。

放一張說明問題的圖:

監聽和建立通信的端口都是7000。套接字描述符對應一個文件,只要組成描述符的任何一個不一樣,就是不同的套接字,比如改變源端口,ip等。

明白socket只是對TCP/IP協議棧操作的抽象,而不是簡單的映射關係,這很重要!

 

http://blog.csdn.net/zztfj/article/details/10103621http://blog.csdn.net/zztfj/article/details/10103621

 

 單機最大的TCP連接數及其修改
一個誤解: 單個服務器程序可承受最大連接數“理論”上是“65535” .

   65535這個數字的由來,很多人想當然地將它與port最大值聯繫起來。的確,TCP的端口數,最大值確實爲65535。但是,這並不代表一個服務器可以接受的連接數就是這個值。很多人之所以把這兩個概念搞混淆是因爲對socket和port沒有更深的認識和理解。我們先來回想一下服務器服務的先後過程:
1、服務器創建監聽socket
2、與對外服務的端口號綁定
3、開始listen
4、客戶端連接到服務器對應的port
5、服務器accept爲新的客戶端產生新的socket
6、基於這個新的socket與客戶端交換數據。
從以上流程來看,最大值爲65535的“端口號”這個重要的東東,我們只用了一次,就是執行bind的時候!而以後創建的socket,說白了就是一個可以進行網絡IO操作的HANDLE而已。通過查看該HANDLE的RemoteEndPoint能查看到遠程客戶端連接的IP和端口號(注意,該端口是遠程客戶端的端口),查看該HANDLE的LocalEndPoint能看到該Socket的Ip和端口就是該服務綁定的IP和端口。所以,accept的socket值與端口號無關,又何來65535的“理論”上限?

好了,已經弄明白了服務器端接收的客戶端連接連接數不受最大端口號65535的限制。但是,在客戶端,應用程序最多可以建立多少個TCP連接呢?以及如何調整系統參數來調整單機的最大TCP連接數。

Windows 下單機的TCP連接數有多個參數共同決定,下面一一介紹:

最大TCP連接數
[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
TcpNumConnections = 0x00fffffe (Default = 16,777,214)

 

以上註冊表信息配置單機的最大允許的TCP連接數,默認爲 16M。這個數值看似很大,這個並不是限制最大連接數的唯一條件,還有其他條件會限制到TCP 連接的最大連接數。

最大動態端口數
TCP客戶端和服務器連接時,客戶端必須分配一個動態端口,默認情況下這個動態端口的分配範圍爲 1024-5000 ,也就是說默認情況下,客戶端最多可以同時發起3977 個Socket 連接。我們可以修改如下註冊表來調整這個動態端口的範圍

[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
MaxUserPort = 5000 (Default = 5000, Max = 65534)

 

最大TCB 數量
系統爲每個TCP 連接分配一個TCP 控制塊(TCP control block or TCB),這個控制塊用於緩存TCP連接的一些參數,每個TCB需要分配 0.5 KB的pagepool 和 0.5KB 的Non-pagepool,也就說,每個TCP連接會佔用 1KB 的系統內存。

系統的最大TCB數量由如下註冊表設置決定

[HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parameters]
MaxFreeTcbs = 2000 (Default = RAM dependent, but usual Pro = 1000, Srv=2000)

非Server版本,MaxFreeTcbs 的默認值爲1000 (64M 以上物理內存)

Server 版本,這個的默認值爲 2000。

也就是說,默認情況下,Server 版本最多同時可以建立並保持2000個TCP 連接。

最大TCB Hash table 數量
TCB 是通過Hash table 來管理的,下面註冊表設置決定了這個Hash table 的大小

HKEY_LOCAL_MACHINE \System \CurrentControlSet \services \Tcpip \Parameters]
MaxHashTableSize = 512 (Default = 512, Range = 64-65536)

這個值指明分配 pagepool 內存的數量,也就是說,如果MaxFreeTcbs = 1000 , 則 pagepool 的內存數量爲 500KB

那麼 MaxHashTableSize 應大於 500 才行。這個數量越大,則Hash table 的冗餘度就越高,每次分配和查找 TCP  連接用時就越少。這個值必須是2的冪,且最大爲65536.

 

IBM WebSphere Voice Server 在windows server 2003 下的典型配置
這是IBM WebSphere Voice Server 的典型配置,大家可以做個參考。原文參見

IBM Web Sphere Voice Server 配置

 

•MaxUserPort = 65534 (Decimal)
•MaxHashTableSize = 65536 (Decimal)
•MaxFreeTcbs = 16000 (Decimal) 
這裏我們可以看到 MaxHashTableSize 被配置爲比MaxFreeTcbs 大4倍,這樣可以大大增加TCP建立的速度。

 

http://www.zhihu.com/question/30772664

 

現在 epoll 單機(4G內存)併發量最大能達到多少?修改

寫補充說明

舉報

 添加評論 分享 • 邀請回答

按投票排序按時間排序

2 個回答

張亞偉,zsummerX, log4z作者.

馬劍飛夢覺孫鵬 等人贊同

按照題主的意思 是根據內存去算一個最大併發的連接數. 那麼首先要找出來單個連接消耗內存的地方.
第一個首先是socket buffer. read 和write 分別有一個, 默認大小在

  • /proc/sys/net/ipv4/tcp_rmem (for read)
  • /proc/sys/net/ipv4/tcp_wmem (for write)

默認大小都是87K和16K, 最低是4K和4K, 最高是2M,2M, 實際使用默認值最低也要保留8K,8K.

然後是邏輯IO緩衝區
就是比如你監聽了recv事件 事件來了 你要有內存可用(一般都是socket建立起就分配好,斷開纔會釋放的).
這個內存是自己寫socket程序時候自己控制的, 最低也要4K,4K, 實際使用8K,8K至少.

現在設定一個優化方案和使用場景, 首先假設4G內存全部爲空閒(系統和其他進程也要內存的....

假如網絡包的大小都可以控制在4K以下, 假設所有連接的網絡都不會擁堵, 或者擁堵時候的總量在4K以下:
一個連接的內存消耗是4+4+4+4=16K
4G/16K=26.2萬併發

假如網絡包的大小都可以控制在8K以下, 假設所有連接的網絡都不會擁堵, 或者擁堵時候的總量在8K以下
一個socket的內存佔用介於 24K ~ 32K之間, 保守的按照32K算 
4G/32K=13.1萬併發, 這個在生產環境作爲一個純網絡層面的內存消耗, 是可以作爲參考的.

假如使用默認配置, 假如所有連接的網絡都出現嚴重擁堵, 不考慮邏輯上的發送隊列的佔用,
使用默認配置是2M+2M+8+8 ~= 4M
4G/4M=1024併發 ( ...
如果考慮到發送隊列也擁堵的話 自己腦補.


如果只是爲了跑分 爲了併發而優化, 沒有常駐的邏輯緩衝區 並且socket的網絡吞吐量很小並且負載平滑, 把socket buffer size設置系統最低.
那麼是
4G/8K = 52.4萬併發 這個應該是極限值了.

編輯於 2015-05-30 6 條評論          

贊同0反對,不會顯示你的姓名

匿名用戶

樓上答的不錯了,記得這個好像是百度的面試題,在羣裏有人問過,當時關注了下,題主可以man下epoll就明白樓上的解釋了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章