我正在尝试学习 ipv4、TCP 和 UDP 协议。我了解到,在 TCP 或 UDP 数据报中分配端口号的目的是确保发送/接收机器将数据报转发到正确的应用程序(进程)。
关于这一点我有两个类似的问题。
首先,如果系统正在运行一个进程的两个实例,会发生什么情况?为了便于讨论,我们假设一台机器正在运行一个 Web 浏览器的两个实例。也许有两个用户登录到终端?或者也许一个用户正在运行一个进程的两个实例。我们还假设这两个进程或用户通过 http 连接连接到同一个 IP。(端口 80)。
在这种情况下,ipv4 如何工作?请求看起来肯定是相同的吗?我们的系统如何知道将响应数据报发送给哪个进程?
其次,作为第一个问题的延伸,如果两个不同的进程使用相同的端口号会发生什么?例如,我们可以有两个不同的应用程序来读取邮件。假设它们使用相同的协议和端口号。在这种情况下,我们的系统如何知道将接收数据发送到哪个邮件进程实例?假设它们都使用相同的 IP 和端口,就像以前一样。
答案1
多个进程不共享端口号。
假设您正在您的机器上设置一个 Web 服务器,比如 Apache。假设您的机器只有一个 IPv4 地址,比如 192.0.2.2。Apache 将请求您的操作系统的 TCP/IP 堆栈允许它在 TCP 端口 80 上打开一个侦听套接字,如果没有其他程序正在使用该端口,则 TCP/IP 堆栈将允许它使用该端口。现在假设在您仍在运行 Apache 时,您(或同一台机器的另一个终端上的另一个用户)尝试在同一台机器上设置 nginx。当 nginx 尝试在 TCP 端口 80 上打开一个侦听套接字时,它将从堆栈收到一条错误消息,指出该端口正在使用中。因此,您必须重新配置 nginx 以使用其他 TCP 端口作为其侦听器,比如 8080。
任何尝试连接到您计算机上的 TCP 端口 80 的客户端都将与 Apache 通信,而不是与 nginx 通信。如果您想连接到 nginx,则必须指定 8080 端口号:“http://192.0.2.2:8080/“
通常,应用仅在侦听器/服务器端指定端口号。因此,当 Web 浏览器连接到 Web 服务器时,Web 浏览器会要求 TCP/IP 堆栈连接到远程 IP 地址上的端口 80,而 TCP/IP 堆栈只会为该连接分配一个任意源端口,使用“临时”端口范围(从 49152 到 65535)中的任何可用(未被其他进程使用)端口号。
答案2
每个 IP 数据包都带有有关其源地址和目标地址的信息。如果它封装了 TCP,则 TCP 部分会带有有关其源 TCP 端口和目标 TCP 端口的信息。UDP 也是如此。我们这样表示:
src-ip:src-port → dst-ip:dst-port
注意:在这个答案中,之前的所有内容:
都应被视为数字 IP 地址,即使它有时看起来像主机名;之后的所有内容:
都应被视为端口号。
反向传输的数据包被认为属于同一连接。这意味着“说”ipA:portA → ipB:portB
从 A 传输到 B 的数据包,“说”ipB:portB → ipA:portA
从 B 传输到 A 的数据包;所有这些都属于我们可以表示为的连接
ipA:portA ↔ ipB:portB
或者等价地
ipB:portB ↔ ipA:portA
这种等价性是有效的,因为建立连接后情况是对称的,哪一端监听、哪一端发起连接都无关紧要。
最重要的是要理解:任何单个连接(本地)由两个地址和两个端口标识。如果数据包报告另一个地址或/和端口,则它属于另一个连接。
当创建数据包时,操作系统会确保新的连接不会“冒充”旧连接。
我说“本地标识”是因为有时 IP 地址和端口在传输过程中会被转换。在这种情况下,在路由的一部分上,连接由一个地址和端口三元组标识,在另一部分上,连接由另一个三元组标识;因此,不存在可以在任何地方标识连接的全局三元组。但在任何给定节点(包括两端)都存在这样的本地三元组,因此始终可以区分单独的连接。
在每一端,相应的操作系统都会跟踪哪个进程为哪个已建立的连接提供服务。
我希望在回答您的具体问题之后,这一点会变得更加清楚。
2 个 Web 浏览器实例[…] 连接到同一个 IP,并具有 HTTP 连接(端口 80)。
第一个连接将是这样的
client:portC ↔ server:80
在 上创建第二个连接时client
,它不能将portC
其作为本地端口。它可以获取尚未用于client
和之间通信的任何有效端口server:80
。从技术上讲,浏览器的第二个实例可能会portC
专门请求,然后将被拒绝。实际上,浏览器不会要求特定的端口,它们会连接到任何临时端口操作系统提供。在我们的例子中,操作系统的工作是提供端口不同的比portC
。
但如果第二个连接的另一端是server2:80
或server:8080
,则可以使用,portC
因为其中任何一个
client:portC ↔ server2:80
client:portC ↔ server:8080
client:portC ↔ server:80
与已采取的不同。
好的,第二个连接将如下所示:
client:portD ↔ server:80
其中portD
不同于portC
。
如果client
是全局可路由的,则不需要转换,服务器会收到“表示”的数据包:
client:portC → server:80
client:portD → server:80
这足以说明它们属于两个不同的连接。响应将“说”
server:80 → client:portC
server:80 → client:portD
分别将它们发送到 ,之后client
操作系统将能够将它们分别传送到适当的进程,因为它知道哪个浏览器实例与哪个连接相关联。
假设 LAN 中有两个客户端连接到server:80
。 可能(偶然)它们都使用相同的本地端口portE
。 在第一个客户端上,此连接建立:
lan_ip1:portE ↔ server:80
第二:
lan_ip2:portE ↔ server:80
它们都通过实施源 NAT 的路由器访问互联网,即lan_ip*
用其全局可路由的替代wan_ip
。为了使 WAN 端的两个连接可区分,路由器不能同时使用portE
这两个连接。在 WAN 端,这些连接将如下所示
wan_ip:portF ↔ server:80
wan_ip:portG ↔ server:80
分别为其中portF
不是portG
(与portE
无关)。
一般来说,路由器的工作是确保 LAN 端不同的连接(由于本地 IP 地址或端口不同)在 WAN 端是可区分的。由于是wan_ip
固定的,而另一端(此处server:80
)不能更改,因此路由器必须分配不同的本地端口。
现在,任何来自 WAN 且“说”的数据包都server:80 → wan_ip:portG
将被转换为 LAN 中“说”的数据包server:80 → lan_ip2:portE
。
现在想象一下两个不同的 LAN 中的两台不同的机器,但每台机器都具有相同的lan_ip
(这两台机器显然无法直接互相访问)。那么可以在每个:
lan_ip:portE ↔ server:80
但
要么他们通过两个不同的全球 IP 地址访问互联网,所以两个不同的路由器将完成它们的工作,并且将
server
看到wan_ip1:portH ↔ server:80 wan_ip2:portI ↔ server:80
即使(偶然)
portH
与相同,这些也是不同的portI
;或者它们通过同一个全局 IP 地址访问互联网(可能通过多个 NAT),因此最终一个路由器将完成其工作,并且将
server
看到wan_ip:portF ↔ server:80 wan_ip:portG ↔ server:80
其中
portF
不是portG
,因为路由器会处理这个问题。
在每种情况下,都server
可以区分这些连接。
此外,server
可能还有一个实现基因转移酶,实际服务器在其后面。任何执行转换的设备都应确保在网络的一部分被视为不同的两个连接在另一部分仍然不同。无论两端之间有多少个 NAT,与server:80
以单独方式开始的连接在实际服务器上都会显示为两个不同的连接。
如果两个不同的进程使用相同的端口号会发生什么? […] 让我们假设它们都使用相同的 IP 和端口,就像以前一样。
上面我们考虑了已建立的连接。现在,因为我不确定你的意思到底是什么,让我们解释一下之前发生的事情:
- 在服务器端,有一个进程(或多个进程,我们将会讲到这个)来监听一些特定的
server_ip
和server_port
。 - 在客户端,进程发起与 的连接
server_ip:server_port
。该连接绑定到client_ip:client_port
,其中client_port
可能经过特别请求或只是随机授予。 握手后,连接建立。服务器进程可以:
- 停止聆听,只为连接服务,
- 继续倾听并服务于联系,
- fork,因此一个进程监听,另一个进程提供连接。
如果两个不同的客户端进程在连接到同一个时尝试使用相同的端口号server_ip:server_port
,则其中一个进程将被拒绝该端口,如上所述。
如果两个不同的服务器进程尝试使用相同的方法server_ip:server_port
来监听它,通常情况下其中一人将被拒绝入境;但:
- 如果监听进程服务于传入连接并停止监听,则另一个进程可以开始监听;
- 如果连接不断涌入,则可能会有许多进程同时使用同一端口来提供服务不同的已建立的连接;
- 一经请求,不同的进程能实际上监听同一个端口来并行处理多个传入连接(参见我的这个答案); 将它与不同的程序一起使用似乎是可能的,但是客户端无法知道哪个程序将为任何特定的连接尝试提供服务,因此几乎没有用;将它与一个程序的几个实例一起使用是有用的。
还有广播,你可以将数据包发送到许多使用相同src-ip:src-port → dst-ip:dst-port
元组的程序(例子)。