在沒接觸過這個問題之前,自然會想到服務器端連接數是由服務器端口號限制的。但這其實是一個很嚴重的誤解,要解決這個問題,必須理解socket的連接過程。
以python為例,tcp服務端socket需要經過如下的初始化步驟:
import socket
# 建立socket對象
s = socket.socket()
# 初始化地址,注意這里用的是一個元組
addr = ("127.0.0.1", 43211)
# 綁定
s.bind(addr)
# 監聽
s.listen(5)
這時服務端的socket就准備好了,進到下一步等待connect的階段:accept。看一下accept的說明:
Accept a connection. The socket must be bound to an address and listening for connections. The return value is a pair
(conn, address)
where conn is a new socket object usable to send and receive data on the connection, and address is the address bound to the socket on the other end of the connection.
accept會返回一個元組,包含一個新的socket對象和客戶端的地址。這個新的對象用於發送或接收數據。
就是說,對於TCP服務端來說,返回的新的socket對象,和最開始用於listen的socket對象是不一樣的。也就是說,最開始綁定了端口和主機的socket對象是專門用於接收連接請求的。
我們實際使用一下這個函數:
# 客戶端運行
import socket
s = socket.socket()
s.connect(("127.0.0.1", 43211))
# 服務端
s.accept()
# (<socket.socket fd=568, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 43211), raddr=('127.0.0.1', 2588)>, ('127.0.0.1', 2588))
再來一遍
# 客戶端運行
import socket
s = socket.socket()
s.connect(("127.0.0.1", 43211))
# 服務端
s.accept()
# (<socket.socket fd=468, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 43211), raddr=('127.0.0.1', 2823)>, ('127.0.0.1', 2823))
可以看到,每次返回的socket的包含了協議,服務端地址+端口號和客戶端地址+端口號。這個五元組唯一標識了這個連接,也就是說限制單機TCP最大連接數的因素,從理論上來說是ip地址范圍*端口范圍,連接數最多是232*216=2^48。但實際情況中這個和設備的內存,一條tcp連接占用的內存有關,端口號的范圍65535並不是單機服務器處理的連接數上限。