目录 ← 首页
CS168

传输层原理

可靠性抽象与目标

许多应用需要可靠性。例如,在互联网上发送文件时,我们希望接收方收到的字节与发送方发送的字节完全相同,且顺序一致。

然而,第三层(网络层)仅提供不可靠的、尽力而为的数据包交付服务。数据包可能会丢失(被丢弃)、损坏、乱序(发送顺序与接收顺序不一致)。数据包也可能会延迟(例如,数据包可能在队列中等待通过链路时被阻塞)。

在极少数情况下,数据包甚至可能会重复 —— 发送方发送一个数据包,而接收方却收到多个副本。这通常发生在路径上的某个路由器出现某种错误时。实际上,这种错误非常罕见。

趣闻:加州大学伯克利分校的 Vern Paxson 是最早发现并报告链路层数据包重复问题的人之一。

我们将利用第四层(传输层)来弥补这一差距,通过开发基于网络所支持的尽力而为数据包抽象的协议,提供应用开发者可使用的可靠抽象。

出于实际原因(将在其他地方讨论),可靠性是在终端主机而非中间路由器上实现的。此外,为了方便起见,可靠性在操作系统中实现,这样应用程序就无需各自重新实现自己的可靠性机制。

img

我们将通过定义至少一次交付来形式化可靠性。在这种模型中,目的地必须无损坏地收到每个数据包至少一次,但可能会收到数据包的多个重复副本。传输层将利用尽力而为的交付服务来提供至少一次交付。然后,基于至少一次交付,我们的协议可以去除重复项,为应用程序提供恰好一次交付。

请注意,可靠交付并不能保证数据包一定会被发送。一台未连接到网络的计算机,无论使用何种可靠性协议,都无法向目的地发送数据。可靠性协议允许放弃并无法发送数据包,但必须向应用程序报告失败。协议不能虚假声称已成功交付数据包。

我们的协议还应具有高效性。更具体地说,协议应尽可能快地交付数据,并且应最小化带宽使用,避免不必要地发送数据包。例如,我们可以通过将每个数据包重发数百次来保证其到达,但这会违反我们高效使用带宽的要求。

传输层目标

在传输层,我们的目标是为应用程序提供便捷的抽象,让开发者的工作更轻松。传输层允许应用开发者从连接的角度思考,而不是从在网络上发送的单个数据包的角度。理想情况下,开发者无需考虑底层网络细节,如将长数据分割成数据包、重发丢失的数据包、超时等。

可靠性只是我们在传输层可能想要实现的多个目标之一。

传输层通过引入端口号来实现终端主机上不同进程之间的解复用,端口号可用于将每个流(连接)与终端主机上的不同进程相关联。

传输层还实现了流量控制和拥塞控制,这将有助于限制数据包的发送速率,分别避免接收器和网络过载。

基于端口的解复用

假设我的个人计算机上有两个应用程序都在与同一台服务器通信。当数据包到达我的个人计算机时,它们具有相同的源 IP 地址(服务器)和相同的目的 IP 地址(我的计算机)。那么如何区分哪些数据包是针对哪个应用程序的呢?

img

为了区分(即解复用)哪些数据包属于哪个应用程序,传输层头部包含一个额外的端口号,可用于标识终端主机上的特定应用程序。

img

当传输层收到一个数据包时,它可以使用端口号来决定应将有效载荷发送到哪个高层应用程序。由于传输层在操作系统中实现,这些端口(有时称为逻辑端口)是应用程序与操作系统网络栈连接的附着点。应用程序知道自己的端口号,操作系统知道所有应用程序的端口号,通过匹配端口号,数据可以在应用程序和操作系统之间明确传输(不会与其他应用程序的数据混淆)。

img

端口号是 16 位的。现代互联网通常采用客户机 - 服务器设计,其中客户机访问服务,服务器提供这些服务。服务器通常在知名端口(端口号 0-1023)上监听请求。客户机知道这些端口,并可以通过它们请求服务。例如,具有知名端口号的应用层协议包括 HTTP(80 端口)和 SSH(22 端口)。

相比之下,客户机可以选择自己的随机端口号(通常是 1024-65535 范围内的端口号)。这些端口号可以随机选择,因为客户机是发起连接的一方,没有人依赖客户机具有固定的端口号(客户机不提供服务)。客户机端口号是临时的,因为连接结束后可以放弃该端口号,不需要永久保留。

字节流抽象

在传输层实现可靠性意味着应用开发者不再需要从在网络上发送的、大小有限的单个数据包的角度思考。相反,开发者可以从可靠的有序字节流的角度思考。发送方有一个无长度限制的字节流,并将此流提供给传输层。然后,接收方收到完全相同的字节流,顺序一致,没有字节丢失。你可以将字节流想象成一根管道,发送方将字节一个接一个地插入管道,这些相同的字节就会一个接一个地出现在接收方的管道末端。发送方和接收方无需考虑重发丢失的数据包或乱序到达的数据包,因为传输层协议会为开发者实现这些功能。

img

UDP 与数据报

有时,应用程序不需要可靠性。例如,考虑一个读取家庭水压的传感器。该传感器每分钟向公用事业公司发送一次读数(包含时间和水压的小型固定大小消息)。这个系统可能不需要数据包按顺序到达(例如,如果读数已经包含时间戳),也可能不需要将长消息分割成数据包的能力(每次读数都很小)。只要大多数读数到达公用事业公司,该系统甚至可能不需要可靠性。

不需要可靠性的应用程序可以在传输层使用UDP(用户数据报协议) 而不是 TCP。UDP 不提供可靠性保证。如果应用程序需要某个数据包到达,必须自己处理重发(传输层不会重发数据包)。UDP 中的消息仅限于单个数据包。如果应用程序想要发送更大的消息,必须自己负责拆分和重组这些消息。不过,UDP 仍然实现了用于解复用的端口概念。

img

在传输层,你可以根据需要选择使用 UDP 或 TCP,但不能同时使用两者。UDP 和 TCP 是现代互联网中的标准传输层协议。

img

其他可靠性设计

TCP 最初是由 Vint Cerf 和 Bob Kahn 在加州大学洛杉矶分校读研究生时实现的。他们因其工作获得了图灵奖、总统自由勋章等荣誉。值得注意的是,最初的 TCP 设计与实际使用的版本非常相似,并且经受住了时间的考验。TCP 的核心思想相当简单,设计也相当优雅(尽管并不完美)。然而,其实现很难做到正确,而且风险很高,因为几乎整个现代互联网都运行在 TCP 之上。

自创建以来,TCP 的许多个别部分都得到了发展(例如,更好的计时器估计算法、更智能的确认、更智能的初始序列号选择、拥塞控制),但核心架构决策和抽象(面向连接的字节流、窗口)保持不变。

TCP 是互联网上的标准可靠性协议,但也存在其他根本不同的方法。

例如,发送方可以利用冗余思想(如纠错码或 RAID 中所见)来更可靠地发送数据。发送方不按原样发送用户数据,而是将数据编码成更多的数据包,并在每个数据包中有意加入冗余。例如,用户可能有 10 个数据包,某个算法可能将这些数据编码成 20 个数据包。该算法可能保证,只要收到 20 个数据包中的任意 15 个,就可以重建原始的 10 个数据包。

更正式地说,编码算法可能接收 k 个数据包,将它们编码成 n 个数据包(其中 n 大于 k),这样只要收到任意 k’ 个数据包(其中 k’ 大于 k 但小于 n),就可以恢复原始的 k 个数据包。

编码方案是一个深奥的话题,包含许多算法(如喷泉码、 Raptor 码),不过我们不会进一步讨论它们。在视频流媒体平台中可以看到它们的实际应用。

(注:文档部分内容可能由 AI 生成)