当单个应用程序打开与单个服务器的多个连接时,如何正确路由数据包?

当单个应用程序打开与单个服务器的多个连接时,如何正确路由数据包?

通常,当不同的机器或同一台机器上的程序向 Internet 发送请求时,它们会打开一个临时端口,以便数据包能够被正确地路由回来。

如果一个程序向同一个网站发送 1000 个请求会发生什么?每个请求都会打开一个单独的临时端口吗?

问题是,如果我发送 2 个间隔开的请求,那么将使用同一个临时端口。操作系统是否会以某种方式知道响应是否仍在进行中,如果之前的端口正在使用中,它是否会打开新的临时端口?

如果某个程序确实同时发出 1000 个请求并打开 1000 个临时端口,那么同时发出的请求数是否会受到端口总数的限制?

答案1

如果一个程序向同一个网站发送 1000 个请求会发生什么?每个请求都会打开一个单独的临时端口吗?

通常,端口与请求无关,而是与连接相关。HTTP 使用 TCP 传输协议(端口就是在该协议中实现的),该协议承载完全自由格式的双向数据流 - 是否继续对多个请求重复使用相同的传输连接,或者是否每次都关闭并打开一个新连接,完全取决于应用程序协议。

说到 HTTP,早期版本(0.9 和官方 1.0)严格要求每个连接只有一个请求/响应,然而,持久连接后来添加并成为 HTTP/1.1 的标准。

较新的 HTTP/2 旨在多路复用多个请求在平行下通过同一个 TCP 连接,同样 HTTP/3 也利用了 QUIC 中的多流支持。

问题是,如果我发送 2 个间隔开的请求,那么将使用同一个临时端口。操作系统是否会以某种方式知道响应是否仍在进行中,如果之前的端口正在使用中,它是否会打开新的临时端口?

操作系统不知道请求,但知道连接。程序通常不会自行实现传输协议 - 它们使用“套接字”编程接口来访问网络,并且每个 TCP 连接(具有自己的端口对)都由套接字文件描述符表示。程序不关心 TCP 握手,也不关心临时端口 - 它只是要求操作系统创建套接字并连接它。

只要程序持有 TCP 连接,操作系统就会保持该连接处于活动状态,并为该套接字保留端口对。只有关闭它(明确关闭或通过退出程序)才会释放临时端口,并通知服务器连接已关闭。

运行netstat -n即可查看操作系统所知的连接列表。

这也以同样的方式适用于 UDP、SCTP 等。即使 UDP 是无连接的,程序仍然可以拥有绑定到特定端口对的 UDP 套接字,并且操作系统将防止它被意外重用。因此,例如,一个完全自行实现 QUIC 或 uTP 传输协议的程序仍然在操作系统提供的 UDP 套接字之上执行此操作以实现多路复用。

如果某个程序确实同时发出 1000 个请求并打开 1000 个临时端口,那么同时发出的请求数是否会受到端口总数的限制?

是的。如果程序想要同时有 1000 个 TCP 连接到同一个目的地,则其可能的端口数量受到限制。

但是如果远程地址和/或远程端口不同,那么本地端口号至少在技术上可以重用。

答案2

如果单个应用向同一 Web 服务器发出 1000 个 HTTP 请求,首先,其 HTTP 客户端代码可能会尝试通过有限数量的 HTTP/2 或 QUIC (HTTP/3) 连接并行处理所有请求,或者在有限数量的 HTTP 1.1 Keep-Alive 连接中部分并行处理并部分序列化这些请求。因此,它可能只会选择建立 4 个同时连接,在这种情况下,它只会打开 4 个临时端口。

如果它确实选择打开大量独立连接,那么它将使用大量临时端口,并且您可能会用尽可用的临时端口。由于临时端口范围从 49152 到 65535,因此您只能同时从同一客户端 IP 地址到同一服务器 IP 地址和端口打开大约 16,384 个 TCP 会话。

为了避免过早重用端口号可能出现的问题,TCP 要求最近关闭的临时端口保持闲置状态 2 分钟。在 2 分钟等待期内被占用的端口被称为时间的等待状态。TCP 堆栈跟踪哪些端口处于时间的等待即使打开它们的进程已经退出或者以其他方式死亡/变成僵尸,也要保持状态。

请注意,TCP 连接的唯一标识是4 元组(destination IP address, destination port, source IP address, source port)。因此,如果 Web 服务器有多个可访问的 IP 地址,或者客户端有多个可用的 IP 地址,那么每对 IP 地址都可能拥有自己的一组临时端口。但是,HTTP 客户端库或 TCP 堆栈中提供的 API 可能并不容易实现这一点。

相关内容