这可能是一个非常基本的问题,但它让我感到困惑。
两个不同的连接套接字可以共享端口吗?我正在编写一个应该能够处理超过10万个并发连接的应用服务器,而且我们知道系统上可用的端口数量大约为60k(16位)。连接的套接字被分配给新的(专用)端口,因此这意味着并发连接的数量受端口数量的限制,除非多个套接字可以共享同一个端口。所以这个问题。
提前感谢您的帮助!
答案 0 :(得分:117)
服务器套接字侦听单个端口。该服务器上所有已建立的客户端连接都与该连接的相同侦听端口在服务器端相关联。建立的连接由客户端和服务器端IP /端口对的组合唯一标识。同一服务器上的多个连接可以共享相同的服务器端 IP /端口对,只要它们与不同的客户端 IP /端口对关联,并且< strong>服务器将能够处理与可用系统资源允许的客户端数量相同的客户端。
在客户端上,新的出站连接通常会使用随机客户端端口,在这种情况下,可能会耗尽如果你在很短的时间内建立了很多连接,那么可用的端口。
答案 1 :(得分:106)
那么,当服务器侦听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.
这很直观,因为从客户端的角度来看,他有一个IP地址,并连接到IP:PORT的服务器。由于客户端连接到端口80,那么他的端口也必须是80?这是一个明智的想法,但实际上不会发生什么。如果这是正确的,我们只能为每个外部IP地址服务一个用户。一旦远程计算机连接,他就会将端口80占用端口80连接,而其他任何人都无法连接。
必须理解三件事:
1。)在服务器上,进程在端口上监听。一旦获得连接,它就会将其交给另一个线程。通信永远不会占用监听端口。
2.)操作系统通过以下5元组唯一标识连接:(本地IP,本地端口,远程IP,远程端口,协议)。如果元组中的任何元素不同,那么这是一个完全独立的连接。
3.)当客户端连接到服务器时,它会选择随机,未使用的高阶源端口。这样,单个客户端可以为同一目标端口提供最多约64k的服务器连接。
因此,这实际上是客户端连接到服务器时创建的内容:
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 -
所以现在有一个进程在端口500上主动侦听(状态:LISTEN)。本地地址是0.0.0.0,这是&#34;侦听所有IP地址&#34;的代码。一个容易犯的错误是只侦听端口127.0.0.1,它只接受来自当前计算机的连接。所以这不是连接,这只是意味着请求绑定()到端口IP的进程,并且该进程负责处理到该端口的所有连接。这暗示了每个计算机只能在一个端口上监听一个进程的限制(有多种方法可以使用多路复用来解决这个问题,但这是一个更复杂的主题)。如果Web服务器正在侦听端口80,则它无法与其他Web服务器共享该端口。
现在,让我们将用户连接到我们的机器:
quicknet -m tcp -t localhost:500 -p Test payload.
这是一个简单的脚本(https://github.com/grokit/quickweb),用于打开TCP套接字,发送有效负载(&#34;测试有效负载。&#34;在这种情况下),等待几秒钟并断开连接。发生这种情况时再次执行netstat会显示以下内容:
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 -
如果您与其他客户端连接并再次执行netstat,您将看到以下内容:
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地址之间永远不会混淆。
答案 2 :(得分:26)
已连接的套接字分配给新的(专用)端口
这是一种常见的直觉,但这是不正确的。连接的套接字未分配给新的/专用端口。 TCP堆栈必须满足的唯一实际约束是(local_address,local_port,remote_address,remote_port)的元组对于每个套接字连接必须是唯一的。因此,只要端口上的每个套接字都连接到不同的远程位置,服务器就可以有许多使用相同本地端口的TCP套接字。
中的“套接字对”段落答案 3 :(得分:7)
理论上,是的。练习,而不是。大多数内核(包括linux)不允许您对已分配的端口进行第二次bind()
。这不是一个非常大的补丁,允许这样做。
在概念上,我们应该区分 socket 和 port 。套接字是双向通信端点,即我们可以发送和接收字节的“事物”。这是一个概念性的事情,在名为“socket”的包头中没有这样的字段。
Port是一个能够识别套接字的标识符。在TCP的情况下,端口是16位整数,但也有其他协议(例如,在unix套接字上,“端口”本质上是一个字符串)。
主要问题如下:如果传入的数据包到达,内核可以通过其目标端口号识别其套接字。这是一种最常见的方式,但它不是唯一的可能性:
因为您正在使用应用程序服务器,所以它可以执行此操作。
答案 4 :(得分:1)
不。在特定时刻不可能共享相同的端口。但是您可以将您的应用程序制作为使其能够在不同的时刻进行端口访问。
答案 5 :(得分:0)
我想没有一个答案能说明过程的每一个细节,所以它是这样的:
考虑一个 HTTP 服务器:
它要求操作系统将端口 80 绑定到一个或多个 IP 地址(如果您选择 127.0.0.1,则只接受本地连接。您可以选择 0.0.0.0 绑定到所有 IP 地址(本地主机,局域网、广域网、IP 版本))。
当客户端连接到该端口时,它会将其锁定一段时间(这就是套接字有积压的原因:它会排队许多连接尝试,因为它们不是即时的)。
然后操作系统选择一个随机端口并将该连接转移到该端口(将其视为一个临时端口,从现在开始将处理所有流量)。
然后释放 80 端口用于下一个连接(首先,它将接受积压中的第一个)。
当客户端或服务器断开连接时,随机端口会保持打开一段时间(远程端为 CLOSE_WAIT,本地端为 TIME_WAIT)。这允许沿路径刷新一些丢失的数据包。该状态的默认时间为 2 * MSL 秒(并且在等待时会消耗内存)。
等待之后,该随机端口再次空闲以接收其他连接。
因此,TCP 甚至不能在两个 IP 之间共享一个端口!