这可能是一个非常基本的问题,但它让我感到困惑。
两个不同的连接套接字是否可以共享一个端口?我正在编写一个应用程序服务器,应该能够处理超过100k并发连接。我们知道系统上可用的端口数量大约为60k(16位)。连接套接字被分配给一个新的(专用)端口,这意味着并发连接数受限于端口数,除非多个套接字可以共享同一个端口。所以问题来了。
这可能是一个非常基本的问题,但它让我感到困惑。
两个不同的连接套接字是否可以共享一个端口?我正在编写一个应用程序服务器,应该能够处理超过100k并发连接。我们知道系统上可用的端口数量大约为60k(16位)。连接套接字被分配给一个新的(专用)端口,这意味着并发连接数受限于端口数,除非多个套接字可以共享同一个端口。所以问题来了。
当服务器监听TCP端口时会发生什么?例如,假设您在80端口上有一个Web服务器。假设您的计算机具有公共IP地址24.14.181.229,试图连接您的人具有IP地址10.1.2.3。这个人可以通过打开到24.14.181.229:80的TCP套接字来连接您。很简单。
直观地(并且错误地),大多数人认为它看起来像这样:
Local Computer | Remote Computer
--------------------------------
<local_ip>:80 | <foreign_ip>:80
^^ not actually what happens, but this is the conceptual model a lot of people have in mind.
Local Computer | Remote Computer | Role
-----------------------------------------------------------
0.0.0.0:80 | <none> | LISTENING
127.0.0.1:80 | 10.1.2.3:<random_port> | ESTABLISHED
首先,让我们使用netstat查看此计算机上正在发生的情况。我们将使用端口500而不是80(因为在端口80上会发生很多事情,但从功能上讲并没有区别)。
netstat -atnp | grep -i ":500 "
如预期所示,输出为空。现在让我们启动一个Web服务器:
sudo python3 -m http.server 500
现在,运行netstat命令的输出如下:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
现在,让我们将用户连接到我们的计算机:
quicknet -m tcp -t localhost:500 -p Test payload.
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:54240 ESTABLISHED -
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:26813 ESTABLISHED -
也就是说,客户端为连接使用了另一个随机端口。因此,IP地址之间永远不会混淆。
SO_REUSEADDR
在相同的IP/端口(通配符或其他方式)上绑定,第二个bind()
仍将失败,并显示WSAEADDRINUSE
。我刚试过了。在同一IP/端口对上有两个TCP服务器监听是没有意义的,即使文档也警告不要这样做。 - Remy Lebeau连接的套接字不会被分配到一个新的(专用的)端口
这是一种常见的直觉,但是它是不正确的。连接的套接字并不会被分配到一个新的/专用的端口。TCP协议栈必须满足的唯一实际约束是(local_address, local_port, remote_address, remote_port)四元组对于每个套接字连接必须是唯一的。因此,服务器可以有许多使用同一个本地端口的TCP套接字,只要该端口上的每个套接字都连接到不同的远程位置即可。
请参见W. Richard Stevens、Bill Fenner和Andrew M. Rudoff所著《UNIX网络编程:套接字联网API》中的“Socket Pair”段落,网址:http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false
bind()
操作在隐式或显式地进行connect()
操作之前进行。 - user207421bind()
只在服务器端在accept()
之前使用。那么客户端也会绑定特定的端口吗? - Sam YCbind()
可以在客户端使用,在 connect()
之前调用。 - Remy Lebeauaccept()
返回的) - Jeremy Friesnerbind()
。如果允许这样做,则需要一个相对较小的补丁。
从概念上讲,我们应该区分socket和port。Socket是双向通信终点,即可以发送和接收字节的 "things"。这是一个概念上的东西,在数据包头中没有名为 "socket" 的字段。
端口是一种标识符,能够标识一个socket。在TCP的情况下,一个端口是一个16位整数,但也有其他协议(例如,在unix sockets上,一个 "port" 本质上是一个字符串)。
主要问题是:如果一个传入的数据包到达了,内核可以通过其目标端口号来确定其socket。这是最常见的方式,但不是唯一的可能性:
因为您正在使用应用程序服务器,所以它将能够执行此操作。
bind()
。 - user207421bind()
设置监听套接字的情况?我可以想象得出,这是很有可能的,但事实上,无论是 WinSock 还是 Posix API 都使用 bind()
调用来进行设置,即使它们的参数设置基本相同。 即使一个API没有这个调用,某种方式你也需要指定从哪里读取传入的字节。 - peterhlisten()
/ accept()
API调用可以以一种区分它们的方法创建套接字,即按其传入端口来区分。 OP的问题可以这样解释,他本质上是在问这个问题。我认为这很现实,但这并不是他的问题字面意思。 - peterhaccept()
返回的套接字会被分配本地IP/端口,即服务器正在侦听的IP/端口,以及客户端在连接之前绑定的远程IP/端口。通常,客户端会在连接之前在其一端绑定到一个随机端口(但这不是必需的,它也可以绑定到特定端口),它不会在服务器端使用随机端口。服务器的端口根本不会被锁定。 - Remy Lebeau不,无法在同一时间共享相同的端口。但是您可以使应用程序以这样的方式运行,使其在不同的时间访问端口。
SO_REUSEADDR
/ SO_REUSEPORT
如果需要)到相同的本地IP/端口,然后连接到不同的服务器。 - Remy Lebeau
64k个连接。”因此,在实践中,如果客户端不会同时两次或多次连接到相同的服务器和端口,则客户端甚至可以拥有超过64K个连接。这是真的吗?如果是的话,那就意味着从客户端侧的单个端口可以连接到许多不同的服务器进程(使套接字连接不同)。因此,总体而言,多个客户端套接字可以驻留在客户端机器上的同一端口上吗?请阅读我对“Remey Lebeau”答案的评论。谢谢:D - Prem KTiw