为什么 TCP 套接字由 4 元组标识?

网络工程 通讯协议 协议理论 UDP 第4层 传输协议
2021-07-27 21:59:24

网络新手在这里。我正在阅读计算机网络(第 3 版)一书,在 3.2 节中,他们正在讨论 UDP 和 TCP 的多路复用/多路分解。

在UDP协议中,一个socket由源IP和源端口唯一标识。

在TCP协议中,socket由源IP、源端口、目的IP和目的端口唯一标识。为什么 TCP 协议需要两条额外的信息才能让接收主机正确解复用该段并将其发送到正确的进程?

我能想到的唯一原因是,如果客户端总是将 TCP 段发送到与连接请求段相同的端口。例如,我的浏览器总是将数据发送到服务器的端口 80,即使服务器已经在不同端口上专门为该会话建立了 TCP 套接字。在这种情况下,TCP 必须使用源 IP 和源端口信息来多路分解到正确的套接字。它不能仅仅依赖源IP信息,因为单个主机可以建立多个会话,但每个会话必须在不同的端口上。

UDP 没有这个问题的原因是因为目标 IP / 端口组合标识了将处理请求的进程所连接到的套接字,因为在 UDP 中没有为请求“生成”多个新套接字。

这是正确的还是我得出了错误的结论?

4个回答

在UDP协议中,一个socket由源IP和源端口唯一标识。

在TCP协议中,socket由源IP、源端口、目的IP和目的端口唯一标识。为什么TCP协议需要两条额外的信息

NB 对于 TCP 术语,套接字是地址-端口对;一对套接字定义了连接(根据RFC 793 p5)

恐怕您对 UDP 有误解,尽管它实际上并没有“套接字”——即使伯克利套接字库这样称呼它们,而且调用地址-端口对是合理的——多路复用本质上与TCP相同的方式。

您可以看到这种情况的典型情况是从一台主机到同一个 DNS 服务器同时进行多个 DNS 解析,其中显然只有源端口号必然不同。您可以看到这与从一个客户端到单个 Web 服务器的多个并发 TCP 连接完全相同。

UDP 具有无连接数据报。主机 A 从地址-端口对发送数据报,定向到 B 的地址-端口对,通常但不总是以镜像方式回复。更宽松地谈论“通信”,它在与 TCP 连接完全相同的 4 元组上运行。

有时您会看到对(协议、源地址、源端口、目标地址、目标端口)的 5 元组的引用,其中协议为 17 表示 UDP,6 表示 TCP 等。这是大多数防火墙、路由器等使用的NAT 和类似的操作来识别这个通信对。

即使服务器已经在不同的端口上专门为该会话建立了一个 TCP 套接字

恐怕您对 TCP 也有误解,可能是因为 TCP 协议 (RFC 793) 的定义与其最常见的实际实现,即 Unix 中使用的 Berkeley Sockets Library 之间的术语冲突,并且一切都来自它。

如果您专注于协议,那就更清楚了:没有“不同的端口”。例如,Web 服务器侦听 1.1.1.1 端口 80。客户端仅从2.2.2.2 端口 56789 发送。每个数据包将是 1.1.1.1:80 到 2.2.2.2:56789,反之亦然; 通过使用 tcpdump/wireshark/etc 查看数据包可以轻松验证。

(要非常简要地离题的伯克利实现中,TCP连接用一个整数通常但容易混淆称为表示sockfd;一个TCP套接字由a表示struct sockaddr的。accept()系统调用很容易混淆讲制作一个“新连接的套接字”,它指的的新连接连接状态下的结构。这个结果的元组在我们的例子中是 (1.1.1.1, 80, 2.2.2.2, 56789)。关于 UDP,该库允许您将 UDP 视为已连接,这是描述两个进程之间的 UDP 数据报交换的一种方便如果完全错误的方式,只是意味着结构记住了远地址-端口对,这在编程术语中使 UDP “连接”看起来就像一个 TCP 连接。请记住,Berkeley 库不仅适用于 IP,而且对几种不同的底层网络系统进行了概括。如果你想跟进这些网络编程术语,我建议 Stack Overflow,它有很多非常称职的网络程序员。)

不幸的是,事情变得令人困惑,因为套接字有两种不同的定义。TCP rfc 使用术语套接字来指代地址和端口的组合,但是 berkerly 套接字及其派生类(当今使用的几乎所有 IP 实际实现都使用的 API)使用术语套接字来指代一种操作系统通信对象。

为什么 TCP 协议需要两条额外的信息才能让接收主机正确解复用该段并将其发送到正确的进程?

这不仅仅是正确过程的问题,而是正确的通信对象的问题。

我能想到的唯一原因是,如果客户端总是将 TCP 段发送到与连接请求段相同的端口。例如,我的浏览器总是将数据发送到端口 80

他们是这样。

即使服务器已经在不同的端口上专门为该会话建立了一个 TCP 套接字。

这似乎是一个常见的误解,可能是由 socket 的不同定义引起的。接受连接会创建一个新的通信对象(Berkerly 套接字术语中的套接字),但不会在服务器上分配新的 ip/端口组合(TCP RFC 术语中的套接字)。

UDP 没有这个问题的原因是因为目标 IP / 端口组合标识了将处理请求的进程所连接到的套接字,因为在 UDP 中没有为请求“生成”多个新套接字。

正确(假设您的段落在 berkerly 套接字意义上使用“套接字”)。

在UDP协议中,一个socket由源IP和源端口唯一标识。

请不要将网络编程(套接字)与网络协议混合使用。

但是,在 UDP 的情况下,您也有这个 4 元组!

TCP 和 UDP 之间的区别在于 UDP 不使用固定连接,因此可以使用一个套接字将数据发送到不同的计算机和/或不同的目标端口。

为此,操作系统仅保存 4 元组的 2 个元素(本地端口的 IP 地址和端口号),而其他 2 个元素(另一台计算机的 IP 地址和端口号)必须由应用程序提供(在该sendto()功能)。

另一方面,TCP 是面向连接的,套接字描述了两台计算机之间的连接。因此一个socket只能用于向某台计算机的某个TCP端口发送数据(使用该send()函数)。

这意味着 4 元组的所有 4 个元素(不仅仅是其中的 2 个)对于套接字都是固定的,因此操作系统可以存储所有 4 个元素,并且应用程序不必提供 4 个元素中的 2 个。

如果您正在阅读的书是 Jim Kurose 的“计算机网络——自上而下的方法”,那么您和我正在阅读同一本书。:-) 仅供参考,这本书实际上说明了基于目标(而非源)IP 和端口来识别 UDP 套接字的二元组。至少,这就是第 7 版的内容。

为了回答您的问题,TCP 是面向连接的,而 UDP 是无连接的。因此,当在主机和服务器之间建立 TCP 请求时,该连接的每一方都希望确定后续请求将使用相同的连接(否则,使用像 TCP 这样的面向连接的协议有什么意义?)。而且,由于与两个数据段相同的目标IP和端口,但是不同的源IP地址和端口将使用两个单独的服务器端插座,只有这样才能保证后续的请求使用相同的插座原始请求是使用对将数据段与其正确的套接字匹配时的源和目标 IP/端口。

相比之下,UDP 不是面向连接的。您不必担心使用与先前请求相同的套接字,并且在确定要将段路由到哪个 UDP 套接字时无需包含源 IP/端口。因此,一个二元组就足够了。