【网络协议笔记】TCP/UDP 协议整理

  1. 概要

运输层协议为运行在不同主机上的应用 进程 之间提供了 逻辑通信(logic communica-tion)功能。
运输层是在 端系统中 而不是在路由器中实现的。

在发送端,运输层将从发送应用程序进程接收到的 报文 转换成运输层分组,称为 报文段(segment)。实现的方法(可能)是将应用报文划分为较小的块,并为每块加上一个 运输层首部 以生成运输层报文段。
运输层将报文段传递给网络层,网络层再将其封装成网络层分组(即数据报)并向目的地发送。网络路由器仅作用于该数据报的网络层字段,不会检查封装在该数据报中的运输层报文段。

网络应用程序可以使用多种运输层协议,因特网有两种协议,即 TCP 和 UDP。
网络层提供了 主机之间 的逻辑通信,而运输层为运行在不同主机上的 进程之间 提供了逻辑通信。
运输协议能够提供的服务常常受制于底层网络层协议的服务模型。

如果网络层协议无法为主机之间发送的运输层报文段提供 时延或带宽 保证,运输层协议也无法为进程之间发送的应用程序报文提供时延或带宽保证。

UDP 的分组也有地方称为 数据报(datagram),不过容易与网络层分组的数据报混淆。
因特网网络层有一个 IP 协议(即网际协议),IP 为主机之间提供了逻辑通信, IP 的服务模型是 尽力而为交付服务(best-effort delivery service),它不确保报文段的交付,不保证报文段的按序交付,不保证报文段中数据的完整性,被称为 不可靠服务(unreliable service)。
TCP 和 UDP 最基本的责任是将两个端系统间 IP 的交付服务扩展为运行在端系统上的两个进程之间的交付服务,扩展到的进程间的交付称为 运输层的多路复用(transport-layer multiplexing) 与 多路分解(demultiplexing)。

进程到进程的 数据交付 和 差错检查 是两种最低限度的运输层服务。

UDP 仅提供了这两种服务。
通过使用 流量控制、序号、确认和定时器,TCP 又确保了正确性和按序,TCP 还提供了 拥塞控制。

  1. 原理
    2.1 多路复用与多路分解

进程通过 套接字(socket)的软件接口向网络发送报文和从网络接收报文,一个进程有 一个或多个 套接字,进程与套接字之间并非总是一一对应的关系。
在源主机从不同套接字中收集数据块,并为每个数据块装上首部信息从而生成报文段,然后将报文段传递到网络层,这些工作称为 多路复用(multiplexing)。
在接收端,运输层检查这些字段,标识出接收套接字,进而将报文段定向到该套接字,将运输层报文段中的数据交付到正确的套接字的工作称为 多路分解(demultiplexing)。
多路复用的要求。

套接字有唯一标识符(UDP 套接字是一个 二元组,TCP 套接字是一个 四元祖)。
每个报文段有特殊的字段来指示该报文段所要交付到的套接字,这些特殊的字段是 源端口字段(source port number field)和 目的端口字段(destination port number field)。

端口号是一个 16 比特的数,范围为 0~65535 之间。0~1023 范围端口称为 周知端口号(well-known port number),是受限制的,保留给 HTTP(80),FTP(21)之类的应用层协议使用,[ RFC 1700 ] 给出了周知端口列表,同时有更新文档 [ RFC 3232 ]。

每个套接字能分配到一个端口号。

2.2 UDP 协议

应用选择 UDP 协议的原因。

关于发送什么数据以及何时发送的应用层控制更为精细。只要应用进程将数据传递给 UDP,UDP 就会将此数据打包进 UDP 报文段并立即将其传递给网络层。TCP 有拥塞机制。
无须连接建立。UDP 不需要任何准备即可进行数据传输。TCP 在开始数据传输前要经过三次握手。
无连接状态。UDP 不维护连接状态。TCP 需要维护连接状态,该状态还包括了接收和发送缓存、拥塞控制参数以及序号与确认号的参数等。
分组首部开销小。UDP 首部有 8 个字节的开销。TCP 首部有 20 字节的开销。

UDP 应用也是可能实现可靠数据传输的,可通过应用程序自身中建立可靠性机制来完成,例如增加确认和重传机制来实现,谷歌的 QUIC 协议在 UDP 之上的应用层协议中实现了可靠性。

2.2.1 UDP 报文段结构

【网络协议笔记】TCP/UDP 协议整理_第1张图片

UDP 报文段结构

UDP 报文段由 [ RFC 768 ] 定义,UDP 首部只有 4 个字段,每个字段由两个字节(共 8 个字节)组成。

通过端口号可以使目的主机将应用数据交给运行在目的端系统中的相应进程(执行分解功能)。
长度包括了首部在内,以字节为单位。

UDP 检验和

UDP 检验和提供了 差错检测 功能,检验和用于确定当 UDP 报文段从源到达目的地移动时,其中的比特是否发生了改变(例如,由于链路中的噪声干扰或者存储在路由器中时引入问题)。

发送方将报文段内容处理为 16 比特的整数序列。
发送方将报文段中所有 16 比特字的和进行 反码 运算,如果有溢出,要被回卷。
接收方计算接收的段的检验和,对比是否一致。
UDP 提供了差错检测,但是对恢复无能为力。

许多链路层协议也提供了差错检测,但是因为不能保证源和目的之间的所有链路都提供了差错检测,所以 UDP 在端到端基础上在运输层提供了差错检测,这也是系统设计中的 端到端原则(end-end principle)。

2.3 可靠数据传输协议原理

数据可以通过一条可靠的信道进行传输,借助于可靠信道,传输数据比特就不会受到损坏或丢失,而且所有数据都是按照其发送顺序进行交付,这种服务抽象是 可靠数据传输协议(reliable data transfer protocol)的责任。

2.3.1 有限状态机

协议中增加了发送方和接收方的 有限状态机(Finite-State Machine,FSM)的定义。
发送方和接收方的 FSM 每个都只有一个状态,协议过程中会从一个状态变迁到另一个状态。

【网络协议笔记】TCP/UDP 协议整理_第2张图片

有限状态机

2.3.2 自动重传

底层信道更为实际的模型是分组中的比特可能受损的模型,在分组的传输、传播或缓存的过程中,这种比特差错通常会出现在网络的物理部件中。
基于这种问题协议中增加了 肯定确认(positive acknowledgment)与 否定确认(negative acknowledgment)(" 请重复一遍 "),这种控制报文使得接收方可以让发送方知道哪些内容被正确接收,哪些内容接收有误并因此需要重复。
在网络环境中,基于这样重传机制的可靠数据传输协议称为 自动重传请求(Automatic Repeat reQuest,ARQ)协议。
ARQ 协议中需要另外三种协议功能处理存在比特差错的情况。

差错检测。检验和。
接收方反馈。分为肯定确认(ACK)和否定确认(NAK)分组,这些分组只需要一个比特长,如 0 表示 NAK,1 表示 ACK。
重传。接收方收到有差错的分组时,发送方将重传该分组文。

【网络协议笔记】TCP/UDP 协议整理_第3张图片

自动重装的状态机

如果收到 ACK 分组,这发送方知道最近发送的分组已被正确接收,协议返回后等待来自上层的数据。
如果收到 NAK 分组,该协议重传上一个分组并等待接收方为响应重传分组而回送的 ACK 和 NAK。
当发送方处于等待 ACK 或 NAK 的状态时,它不能从上层获得更多的数据,这样的协议被称为 等停(stop-end-wait)协议。

2.3.3 序号

协议中的 ACK 和 NAK 分组也存在受损的可能性,当然也可以通过重传当前数据分组解决,但是这样就在发送方和接收方的信道中引入了 冗余分组(duplicate packet),这种分组的根本困难在于接收方无法知道它上一次发送的 ACK 和 NAK 是否被发送方正确收到,它无法事先知道接收到的分组是新的还是一次重传。
为解决这个问题,数据分组中增加了一个新字段,让发送方对其数据分组编号,即分组的 序号(sequence number)。
针对简单协议(例如停等协议),只需 0、1 两个序号来回切换即可,而且 ACK 的序号切换已经可以实现与 NAK 同样的效果,因此可以去除 NAK。

【网络协议笔记】TCP/UDP 协议整理_第4张图片

加入序号的状态机

2.3.4 倒计数定时器

在今天的计算机网络中并不罕见的是底层信道存在丢包的情况。
假设发送方传输一个数据分组,该分组或者接收方对该分组的 ACK 发生了丢失,那么发送方都收不到应当到来的接收方的响应。
针对这种情况,协议增加了发送方等待足够长的时间以便确定分组已丢失,进行重传该数据分组。

但是如果在重传的过程中,之前的分组恢复了,那么就又引入了 冗余数据分组(duplicate data packet)的可能性,不过这种情况,序号足以处理。

实现这种基于时间的重传机制,称为 倒计数定时器,在一个给定的时间量过期后,可中断发送方。

每次发送一个分组(包括第一次分组和重传分组),则启动一个定时器。
响应定时器中断。
终止定时器。

【网络协议笔记】TCP/UDP 协议整理_第5张图片

无丢包操作

【网络协议笔记】TCP/UDP 协议整理_第6张图片

分组丢失

【网络协议笔记】TCP/UDP 协议整理_第7张图片

丢失 ACK

【网络协议笔记】TCP/UDP 协议整理_第8张图片

过早超时

在检验和、序号、定时器、肯定和否定确认分组这些技术中,每种机制都在协议的运行中起到必不可少的作用,至此得到一个可靠数据传输协议。

2.3.5 流水线协议

不以停等方式运行,允许发送方发送多个分组而无需等待确认。
因为允许从发送方向接收方输送的分组可以被看成是填充到一条流水线中,这种技术被称为 流水线(pipelining)协议,它对可靠数据传输协议的影响有。

增加了序号的范围,因为每个输送中分组(不计算重传)必须有一个唯一的序号,而且也许有多个在输送中的未确认报文。
协议的发送方和接收方两端也许不得不缓存多个分组,发送方最低限度应当能缓冲那些已发送但没有确认的分组。接收方同理。
所需序号范围和对缓冲的要求取决于数据传输协议如何处理丢失、损坏及延时过大的分组。

解决流水线的差错恢复的基本方法是 回退 N 步(Go-Back-N,GBN)和 选择重传(Selective Repeat,SR)。

回退 N 步(GBN)

在 回退 N 步 协议中,允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它也受限于 在流水线中未确认的分组数不能超过某个最大允许数 N。

【网络协议笔记】TCP/UDP 协议整理_第9张图片

GBN 中发送方序号

[0,base-1](蓝色)段内对应于已经发送并被确认的分组。

[base,nextseqnum-1](绿色)段内对应于已经发送但未被确认的分组。

[nextseqnum,base+N-1](黄色)段内的序号能用于那些要被立即发送的分组,如果有数据来自上层的话。

=base+N(灰色)不能使用,直到流水线中未被确认的分组(特别是序号为 base 的分组)已得到确认为止。
那些已被发送但未被确认的分组的许可序号范围可以被看成是一个在序号范围内长度为 N 的窗口。

随着协议的运行,该窗口在序号空间向前滑动。N 被称为 窗口长度,GBN 协议也被称为 滑动窗口协议。

GBN 发送方需要响应的三种事件。

上层的调用。需要检查是否已经有 N 个已发送但未被确认的分组,如果未满则产生分组并发送,如果已满,则隐式指示上层该窗口已满,实际实现中,发送方更可能缓存这些数据。
收到一个 ACK。对序号为 n 的分组的确认采取 累积确认(cumulative acknowledgment)的方式,表明接收方已接收到序号为 n 的以前且包括 n 在内的所有分组。
超时事件。来源于出现丢失和时延过长分组时发送方的行为。

如果出现超时,那么发送方重传所有已发送但未确认过的分组。
发送方只使用一个定时器,如果收到 ACK,但仍有已发送但未确认的分组,则定时器被重新启动。
如果没有已发送但未确认的分组,停止该定时器。

GBN 接收方,如果接收到一个序号为 n 的分组,并且按序(即上次交付给上层的数据是序号为 n-1 的分组),则接收方为分组 n 发送一个 ACK,并将该分组中的数据部分交付到上层。其他情况,则丢弃该分组,并为最近按序接收的分组重新发送 ACK。

选择重传(SR)

GBN 协议允许发送方用多个分组 " 填充流水线 ",用以避免停等协议所提到的信道利用率问题,但是当窗口长度和带宽时延积都很大时,在流水线中会有很多的分组,单个分组的差错就能引起 GBN 重传大量的分组,很多分组都没有必要重传。

选择重传 协议,通过让发送方仅重传那些它怀疑在接收方出错(即丢失或损坏)的分组而避免不必要的重传。

2.4 TCP

TCP 基于可靠数据传输协议基本原理,其中包括差错检测、重传、累积确认、定时器以及用于序号和确认号的首部字段。TCP 定义在 [ RFC 793; RFC 1122; RFC 1323; RFC 2018; RFC 2581 ] 中。
TCP 是 面向连接的(connection-oriented),连接的双方都将初始化与 TCP 连接相关的许多 TCP 状态变量。
TCP 是一条逻辑连接,只在端系统中运行,所以中间的网络元素不会维护 TCP 连接状态。
TCP 连接提供的是 全双工服务(full-duplex service)。
TCP 连接总是 点对点(point-to-point)的,即在单个发送方与单个接收方之间的连接。
TCP 连接的建立,总是进行 三次握手(three-way handshake),前两次报文段不包含应用层数据,第三个报文段才包含。
TCP 将数据引导到连接的 发送缓存(send buffer)里,发送缓存是发起三次握手期间设置的缓存之一。

TCP 接下来会不时的从缓存中取出一块数据传递到网络层,TCP 可从缓存中取出并放入报文段中的数据数量受限于 最大报文段长度(Maximum Segment Size,MSS)。

MSS 通常根据最初确定的由本地发送主机的最大链路层帧长度(即 最大传输单元(Maximum Transmission Unit,MTU))来设置。
MSS 是指在报文段里 应用数据的最大长度,而不是指包括首部的 TCP 报文段的最大长度。
连接的每一端都有各自的 发送缓存 和 接收缓存。

2.4.1 TCP 报文段结构

TCP 报文段由首部字段和一个数据字段组成。因为 MSS 限制了报文段数据字段的最大长度,通常一个大文件,会被划分为长度为 MSS 的若干块(最后一块通常小于 MSS)。
TCP 首部字段一般是 20 个字节。

【网络协议笔记】TCP/UDP 协议整理_第10张图片

TCP 报文结构

源端口号和目的端口号用于多路复用/分解。
TCP 也包括检验和字段。
TCP 还包含了 32 比特的 序号字段(sequence number field)和 32 比特的 确认号字段(acknowledgment number field),用来实现可靠数据传输协议服务。
16 比特的 接收窗口字段(receive window field),该字段用于流量控制。该字段用于指示接收方愿意接受的字节数量。
4 比特的 首部长度字段(header length field),该字段指示了以 32 比特的字为单位的 TCP 首部长度。

由于 TCP 选项字段的原因,TCP 首部长度可变。(通常选项字段为空,TCP 首部典型长度为 20 字节)

可选和变长的 选项字段(option field),该字段用于发送方与接收方协商最大报文长度(MSS)时,或在高速网络环境下用作窗口调节因子时使用。首部字段中还定义了一个时间戳选项。
6 比特的 标志字段(flag field)。

ACK 比特用于指示确认字段中的值是有效的,即该报文段包括一个对已被成功接收报文段的确认。
RST、SYN 和 FIN 比特用于连接建立和拆除。
在明确阻塞通告中使用了 CWR 和 ECE 比特。
当 PSH 比特被置位时,指示接收方应立即将数据交给上层。
URG 比特用来指示报文段里存在着被发送端的上层实体置为 " 紧急 " 的数据。

紧急数据的最后一个字节由 16 比特的 紧急数据指针字段(urgent data pointer field)指出。

序号

TCP 把数据看成一个 无结构的,有序 的 字节流,序号是建立在传送的字节流之上的。

一个报文段的 序号(sequence number for a seqment)是该报文段首字节的字节流编号。
假定数据流由一个包含 500000 字节的文件组成,其 MSS 为 1000 字节,TCP 将该数据流构建为 500 个报文段,TCP 会隐式地对数据流中的每一个字节编号,数据流的首字节为编号 0,第一个报文段分配序号(编号)为 0 ,第二个分配为 1000,以此类推。每一个序号被填入相应的 TCP 报文段首部的序号字段中。

确认号

TCP 是全双工的,主机 A 向 B 发送数据的同时,也许也接收到来自主机 B 的数据(都是同一条 TCP 连接的一部分)。从主机 B 到达的每个报文段中都有一个序号用于从 B 流向 A 的数据。主机 A 填充进报文段的确认号是主机 A 期望从主机 B 收到的下一字节的序号。
假设主机 A 已收到了来自主机 B 的编号为 0~535 的所有字节,同时它打算发送一个报文段给主机 B,这时的主机 A 需要等待主机 B 的数据流中字节 536 及以后的字节,所以主机 A 就会在发往主机 B 的报文段的确认号中填入 536。
假设主机 A 已收到了来自主机 B 的编号为 0~535 和 900~1000 的字节,主机 A 仍然会在发往主机 B 的报文段的确认号中填入 536。因为 TCP 只确认该流中至第一个丢失字节为止的字节,所以 TCP 被称为 累积确认(cumulative acknowledgment)。
假设先收到 900~1000 的字节,TCP RFC 并没有为此明确规定任何规则,将处理方式交给实现 TCP 的编程人员,一般会有两种处理方式。

接收方立即丢弃失序报文段。
接收方保留失序报文段,并等待缺少的字节以填补该间隔。(网络带宽更为有效)

流量控制

TCP 为它的应用程序提供了 流量控制服务(flow-control service)以消除发送方使接收方缓存溢出的可能性。

流量控制是一个速度匹配服务,即发送方的发送速率与接收方的读取速率相匹配。

TCP 通过让发送方维护一个称为 接收窗口(receive window)的变量来提供流量控制。
假设主机 A 通过一条 TCP 连接向主机 B 发送一个大文件。

主机 B 为该连接分配了一个接收缓存,并用 RcvBuffer 表示其大小。
从网络中到达的并且已放入主机 B 接收缓存中的数据流的最后一个字节的编号为 LastByteRcvd。
主机 B 上的应用进程从缓存读出的数据流的最后一个字节的编号为 LastByteRead。

【网络协议笔记】TCP/UDP 协议整理_第11张图片

接收窗口 rwnd

TCP 不允许已分配的缓存溢出,所以 LastByteRcvd - LastByteRead <= RcvBuffer。

如上图所示,假设 RcvBuffer 大小为 4,LastByteRead 为 1,那么 LastByteRcvd(6) - LastByteRead(1) > RcvBuffer(4) 缓存溢出。
假设与上图保持一致,LastByteRead 为 3,那么 6 -3 < 4,则不会溢出。

接收窗口(空闲空间)用 rwnd 表示,根据缓存可用空间的数量来设置,即 rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]。rwnd 随着时间变化,是动态的。

假设与上图保持一致,则 rwnd 为 1。

当 rwnd = 0 时,说明主机 B 的接受缓存已满,那么假设主机 B 将 rwnd = 0 通告给主机 A 之后,就再也不传递任何数据给 A,那么就算主机 B 上的应用进程将缓存清空,主机 A 也不可能知道,即主机 A 被阻塞再也不能发送数据。

为了解决这个问题,TCP 规范中要求,但主机 B 的接受窗口为 0 时,主机 A 继续发送只有一个字节数据的报文段,这些报文段将会被接收方确认,最终缓存将开始清空,并且确认报文里将包含一个非 0 的 rwnd 值。

2.4.2 TCP 连接管理
2.4.2.1 三次握手

【网络协议笔记】TCP/UDP 协议整理_第12张图片

三次握手

TCP 连接的建立会进行三次握手(three-way handshake)。

第一步,TCP 发送一个特殊的 TCP 报文段,该报文段不包含应用层数据。

报文段首部的标志字段中的 SYN 设置为 1。这个报文段又被称为 SYN 报文段。
随机选择一个起始序号(c)放置于报文段的序号字段中。
该报文段被封装在一个 IP 数据报中,并发送给服务器。

第二步,IP 数据报达到服务器(接收方)主机,提取出 TCP SYN 报文段,为该 TCP 连接分配 TCP 缓存和变量(在完成三次握手的第三步之前分配这些缓存和变量,使得 TCP 易于受到称为 SYN 洪泛的拒绝服务攻击),这个允许连接的报文段也不包含应用程序数据,这个报文段发送给客户端。

首部的标志字段中的 SYN 设置为 1,ACK 设置为 1,该报文段被称为 SYNACK 报文段。
确认号字段被置为 c+1。
选择自己的序号为 s,并放置到 TCP 报文段首部的序号字段中。

第三步,客户端收到 SYNACK 报文段(第一步的发送方),同样给该连接分配缓存和变量。发送另外一个报文段(这个报文段是对服务器允许连接的报文段的确认)。

确认号字段被置为 s+1。
首部的标志字段中的 SYN 设置为 0,表示连接已经建立。

ACK 设置为 1。
序号为 c+1。
该阶段可以在报文段负载中携带客户到服务器的数据。

三次握手的原因

TCP 的连接因为是全双工的,包含了客户端和服务器两端,发送消息两个方向的连接都要建立成功。为了保证双向连接都成功,三次通信是最少的次数。大于三次就浪费资源了。

三次握手的状态转换

在一个 TCP 连接的生命周期内,运行在每台主机中的 TCP 协议在各种 TCP 状态(TCP State)之间变迁。

【网络协议笔记】TCP/UDP 协议整理_第13张图片

TCP 状态转换

客户端状态

客户端 TCP 开始时处于 CLOSED(关闭)状态。
向服务端中的 TCP 发送一个 SYN 报文段,则进入 SYN_SENT 状态。
客户端收到服务端允许连接的报文段(SYNACK )的确认后,进入 ESTABLISHED(已建立)状态。
处于 ESTABLISHED 状态就可以发送和接受包含有效载荷数据(应用层数据)的 TCP 报文段了。

服务端状态

服务端 TCP 开始时处于 CLOSED(关闭)状态。
收到客户端发送过来的 SYN 消息,进入 SYN_RCVD 状态,向客户端发送一个 SYNACK 消息。
服务端收到客户端允许连接的报文段的确认后,进入 ESTABLISHED(已建立)状态。
处于 ESTABLISHED 状态就可以发送和接受包含有效载荷数据(应用层数据)的 TCP 报文段了。

2.4.2.2 四次挥手

【网络协议笔记】TCP/UDP 协议整理_第14张图片

四次挥手

第一步,客户端 TCP 发送一个 FIN 被设置为 1 的报文段。

随机选择一个起始序号(c)放置于报文段的序号字段中。
该报文段被封装在一个 IP 数据报中,并发送给服务器。
客户端进入 FIN-WAIT-1(终止等待 1)状态。

第二步,服务器收到连接释放报文。

服务端首部的标志字段中的 FIN 设置为 1,ACK 设置为 1。
确认号字段被置为 c+1。
选择自己的序号为 s0,并放置到 TCP 报文段首部的序号字段中。
服务端就进入了 CLOSE-WAIT(关闭等待)状态。
TCP 服务端通知高层的应用进程,客户端向服务器的方向已释放,目前处于半关闭状态,即客户端已经没有数据发送,但服务端若发送数据,客户端依然要接受。
客户端收到服务端的确认请求后,客户端就进入 FIN-WAIT-2(终止等待 2)状态,等待服务端发送连接释放报文(在这之前还需要接收服务端发送的最后的数据)。

第三步,服务端将最后的数据发送完毕后,向客户端发送连接释放报文。

首部的标志字段中的 FIN 设置为 1,ACK 设置为 1。
确认号字段被置为 c+1。
选择自己的序号为 s1,并放置到 TCP 报文段首部的序号字段中。
服务端进入 LAST-ACK(最后确认)状态,等待客户端确认。

第四步,客户端收到服务端的连接释放报文后,发出确认。

首部的标志字段中的 FIN 设置为 1,ACK 设置为 1。
确认号字段被置为 s1+1。
序号为 c+1。
客户端就进入了 TIME-WAIT(时间等待)状态。
此时 TCP 连接还没有释放,必须经过 2MSL(最长报文段寿命,一个发送和一个回复所需的最大时间)的时间后,进入 CLOSED 状态。
服务端收到客户端发出的确认,立即进入 CLOSED 状态。

服务端结束 TCP 连接的时间要比客户端早一些。

你可能感兴趣的:(安全)