TCP: 两个不同的套接字可以共享一个端口吗?

这可能是一个非常基本的问题,但它使我困惑。

两个不同的连接套接字可以共享一个端口吗?我正在编写一个应用服务器,它应该能够处理超过100k 的并发连接,我们知道系统上可用的端口数大约是60k (16bit)。已连接的套接字被分配给一个新的(专用)端口,因此这意味着并发连接的数量受到端口数量的限制,除非多个套接字可以共享相同的端口。所以问题是。

170782 次浏览

已连接的套接字被分配给一个新的(专用)端口

这是一个常见的直觉,但它是不正确的。连接的套接字不会分配给新的/专用的端口。TCP 堆栈必须满足的唯一实际约束是,每个套接字连接的元组(local _ address、 local _ port、 remote _ address、 remote _ port)必须是唯一的。因此,只要端口上的每个套接字都连接到不同的远程位置,服务器就可以使用相同的本地端口拥有许多 TCP 套接字。

请参阅“ UNIX 网络编程: 套接字网络 API”一书中的“套接字对”段落 理查德·史蒂文斯: http://books.google.com/books? id = ptSC4lpwGA0C & amp; lpg = PA52 & amp; dq = socket% 20double% 20tuple & amp; pg = PA52 # v = onepage & amp; q = socket% 20double% 20tuple & amp; f = false”rel = “ nofollow noReferrer”> http://books.google.com/books?id=ptsc4lpwga0c&lpg=pa52&dq=socket%20pair%20tuple&pg=pa52#v=onepage&q=socket%20pair%20tuple&f=false

服务器套接字侦听单个端口。该服务器上所有已建立的客户端连接都与该连接的相同侦听端口 在服务器端相关联。已建立的连接通过客户端和服务器端 IP/端口对的组合唯一标识。同一台服务器上的多个连接可以共享相同的 服务器端 IP/Port 对,只要它们与不同的 客户端 IP/Port 对相关联,而且 服务器将能够在可用系统资源允许的情况下处理尽可能多的客户机。

客户端上,新的出站连接通常使用随机的 客户端端口,在这种情况下,如果您在短时间内建立了大量连接,就有可能耗尽可用端口。

理论上,是的。练习,不行。大多数内核(包括 linux)不允许您将第二个 bind()分配到已经分配的端口。这并不是一个很大的补丁,使这个允许。

在概念上,我们应该区分 插座左舷。套接字是双向通信端点,也就是我们可以发送和接收字节的“事物”。这是一个概念性的东西,在一个名为“套接字”的包头中没有这样的字段。

端口是一个能够识别套接字的标识符。对于 TCP,端口是一个16位整数,但是还有其他协议(例如,在 unix 套接字上,“端口”本质上是一个字符串)。

主要问题如下: 如果传入数据包到达,内核可以通过它的目的端口号来识别它的套接字。这是最常见的方式,但不是唯一的可能性:

  • 套接字可以通过传入数据包的目标 IP 来识别。例如,如果我们有一个同时使用两个 IP 的服务器,就是这种情况。然后,我们可以运行,例如,不同的网络服务器在相同的端口,但在不同的 IP。
  • 套接字也可以通过它们的 来源端口和 ip 来识别。在许多负载平衡配置中都是这种情况。

因为您使用的是应用程序服务器,所以它能够做到这一点。

端口上的 TCP/HTTP 监听: 多个用户如何共享同一个端口

那么,当服务器监听 TCP 端口上的传入连接时会发生什么情况呢?例如,假设您在80端口上有一个 Web 服务器。假设您的计算机的公共 IP 地址为24.14.181.229,而试图连接到您的人的 IP 地址为10.1.2.3。这个人可以通过打开一个 TCP 套接字到24.14.181.229:80来连接到您。很简单。

凭直觉(也是错误的) ,大多数人认为它看起来是这样的:

    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个元组唯一地标识连接: (local-IP、 local-port、 remote-IP、 remote-port、 protocol)。如果元组中的任何元素不同,那么这是一个完全独立的连接。

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(State: LISTEN)。本地地址是0.0.0.0,这是“监听所有 ip 地址”的代码。一个很容易犯的错误是只侦听端口127.0.0.1,它将只接受来自当前计算机的连接。所以这不是一个连接,这只是意味着一个被请求绑定()到端口 IP 的进程,该进程负责处理到该端口的所有连接。这暗示了一个端口上每台计算机只能监听一个进程的局限性(使用多路复用可以绕过这个局限性,但这是一个更复杂的主题)。如果一个 Web 服务器正在监听端口80,它就不能与其他 Web 服务器共享该端口。

现在,让我们将一个用户连接到我们的机器:

    quicknet -m tcp -t localhost:500 -p Test payload.

这是一个简单的脚本(https://github.com/grokit/quickweb) ,它打开一个 TCP 套接字,发送有效负载(“ Test payload”)在这种情况下) ,等待几秒钟并断开连接。在这种情况下再次执行 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 地址之间永远不会混淆。

没有。不可能在特定的时刻共享同一个端口。但是您可以使您的应用程序在不同的时刻进行端口访问。

我想没有一个答案能够说明整个过程的每一个细节,所以就这样吧:

考虑一个 HTTP 服务器:

  1. 它要求操作系统将端口80绑定到一个或多个 IP 地址(如果选择127.0.0.1,则只接受本地连接)。您可以选择0.0.0.0来绑定到所有的 IP 地址(本地主机、本地网络、广域网络、两个 IP 版本)。

  2. 当一个客户端连接到该端口时,它会将其锁定一段时间(这就是套接字有一个待办事项列表的原因: 它排队等待多次连接尝试,因为它们不是即时的)。

  3. 然后,操作系统选择一个随机端口,并将该连接传输到该端口(将其视为一个临时端口,从现在起将处理所有通信)。

  4. 然后为下一个连接释放端口80(首先,它将接受待办事项列表中的第一个连接)。

  5. 当客户端或服务器断开连接时,随机端口将保持打开状态一段时间(远程端为 CLOSE _ WAIT,本地端为 TIME _ WAIT)。这允许冲洗沿路丢失的数据包。该状态的默认时间为2 * MSL 秒(并且在等待时会消耗内存)。

  6. 等待之后,该随机端口再次空闲,可以接收其他连接。

因此,TCP 甚至不能在两个 IP 之间共享一个端口!

绝对不行,因为即使是多个连接也可能有相同的端口,但是它们有不同的 IP 地址