Home 传输层 Transport Layer
Post
Cancel

传输层 Transport Layer

传输层协议概述


传输层 (Transport layer) 又称运输层。传输层向它上面的应用层提供通信服务,即实现可靠传输,其中包括差错控制、顺序控制、拥塞控制等。

网络层实现主机之间的逻辑通信。而传输层则实现应用进程之间的逻辑通信实现真正的端到端通信,即应用进程之间的通信。

在一台主机中经常有多个应用进程同时分别和另一台主机中的多个应用进程通信。这表明传输层有一个很重要的功能,复用 (multiplexing) 和分用 (demultiplexing)

传输层的两个主要协议

传输层主要的协议有 TCP 协议(可靠的传输协议)以及 UDP 协议(不可靠的传输协议)

按照 OSI 的术语,传输的数据单位称为运输协议数据单元 (Transport Protocol Data Unit, TPDU)。但在 TCP/IP 体系中,则根据所使用的协议是 TCP 或 UDP,分别称之为 TCP 报文段 (segment) 或 UDP 用户数据报

Transmission Control Protocol, TCP 协议

可靠的传输协议,其提供面向连接的服务。在传送数据之前,必须先建立连接,传送结束后要释放连接。由于 TCP 要提供可靠的、面向连接的传输服务,因此不可避免地增加了许多的开销,如确认、流量控制、计时器以及连接管理等

基于 TCP 的典型应用协议有 HTTP、FTP、…

User Datagram Protocol, UDP 协议

不可靠的传输协议传输数据之前不需要先建立连接。与 TCP 相比,其效率更高,但可能出现数据错误、丢包以及顺序错误等问题

基于 UDP 的典型应用协议有 DNS、RIP、…

传输层的接口

应用层多个应用进程通过传输层发送数据,这就是复用

传输层收到的数据必须交付给指明的应用程序,这就是分用

传输层必须提供区分上层应用程序的手段,这就是协议端口 (protocol port),简称为端口 (port)。这种在协议栈层间的抽象的协议端口是软件端口,和路由器或交换机上的硬件端口是完全不同的概念。硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层的各种协议进程与传输实体进行层交互的地点

TCP/IP 协议使用 16 位整数作为端口号,但端口号只具有本地意义,只是为了标志本计算机应用层中的各个进程在和传输层交互时的层间接口。

Port 0 ~ 1023

熟知 (well-known) 端口号或系统端口号,如 HTTP 服务使用 80,FTP 服务使用 21 等。

Port 1024-49151

登记端口号,供没有熟知端口号的应用程序使用,必须在 IANA 登记,以防止重复。

Port 49152 ~ 65535

客户端口号或短暂端口号,供客户进程临时使用。

用户数据报协议 UDP


UDP 只在 IP 的数据报服务之上增加了很少一点的功能,即端口和差错检测。其为不可靠传输协议,但具有自身特点,与 TCP 分别面对不同的应用。

UDP 协议的特点有:

  • 无连接,即发送数据之前不需要建立连接
  • 使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制
  • 面向报文的
  • 没有拥塞控制,很适合多媒体通信的要求
  • 支持一对一、一对多、多对一和多对多的交互通信
  • 首部开销小,只有 8 个字节

UDP 首部格式

IP 首部的协议字段为 17 时,数据为 UDP 的报文段

UDP 数据报包括 2 个字段,即首部和数据字段。

首部共有 4 个字段,8 个字节。各字段分别为:

  • 源端口,2 个字节,表示源端口号
  • 目的端口,2 个字节,表示目的端口号
  • 长度,2 个字节,表示 UDP 数据报的长度
  • 校验和。2 个字节,表示 UDP 数据报的校验和

UDP 计算校验和

UDP 计算检验和的方法和计算 IP 数据报首部检验和的方法相似。但不同的是 IP 数据报的检验和只检验 IP 数据报的首部,但 UDP 的检验和是把首部和数据部分一起都检验

传输控制协议 TCP


TCP 主要的特点有:

  • TCP 是面向连接的传输层协议,即传输数据前必须先建立连接,数据传输完后要释放连接
  • 每一条 TCP 连接只能有两个端点 (endpoint),每一条 TCP 连接只能是点对点、一对一的
  • TCP 提供可靠交付的服务,即无差错、不丢失、不重复、按序到达
  • TCP 提供全双工通信,即在一个连接上,通信双方可同时向对方传输数据
  • 面向字节流,这里的流指的是流入到进程或从进程流出的字节序列。应用程序以数据块为单位与 TCP 交互,但 TCP 将其视为无结构的字节流。这导致发送方应用程序发出的数据块与接收方应用进程收到的数据块可能没有一一对应关系,但数据保一致。

需要注意的是,TCP 连接是一条虚拟连接,而不是一条真正的物理连接。TCP 对应用进程一次把多长的报文发送到 TCP 的缓存中是不关心的。TCP 会根据对方给出的窗口值和当前网络的拥塞程度来决定一个报文段应该包含多少个字节,而不是像 UDP 发送的报文长度是由应用程序给出的。其可以把太长的数据块划分短一些再传送,也可以等累计到有足够多的字节后再构成报文段发送出去

TCP 的连接

TCP 把连接作为最基本的抽象。每一条 TCP 连接都有两个端点,TCP 连接的端点不是主机,不是主机的 IP 地址,不是应用进程,也不是传输层的协议端口。TCP 连接的端点叫做套接字 (socket) 或插口

端口号拼接到 (contatenated with) IP 地址即扣成了套接字

  • 套接字 socket ::= (IP 地址 : 端口号)

每一条 TCP 连接唯一地被通信两端的两个端点所确定,即:

  • TCP 连接 ::= {socket1, socket2} = {(IP1: port1), (IP2: port2)}

TCP 报文段的首部格式


IP 首部的协议字段为 06 时,数据为 TCP 的报文段

TCP 报文段首部有 20 个字节是固定的,后面有 4n4n 字节是根据需要而增加的选项。因此 TCP 首部的最小长度是 20 字节

源端口 Source port

2 字节,源端口。

目的端口 Destination port

2 字节,目的端口。

序号字段 Sequence number

4 字节,在 TCP 连接中传输的数据流中的每一个字节都有序号。序号字段指本报文段所发送的数据的第一个字节的序号,以字节为单位。

确认号字段 Acknowledgement number

4 字节, 期望收到对方的下一个报文段的数据的第一个字节的序号。TCP 连接是全双工,即通信双方可互相发送数据,因此应答与数据一同发送给对方。

若收到对方的报文段中序号为 501,数据长度为 200 字节,则返回报文段确认号 701。

首部长度/数据便宜 TCP header length

4 位,TCP 报文段的数据起始位置的偏移,也就是首部的长度,单位是 32 位字(4 字节)。

紧急 URG

1 位,为 1 时,紧急指针字段有效,表明有紧急数据,应尽快传送。

确认 ACK

1 位,为 1 时,确认号字段有效。

推送 PSH

1 位,为 1 时,接收方将尽快向应用进程交付此报文段,而不是等整个缓存填满。

复位 RST

1 位,为 1 时,表明 TCP 连接出现严重差错,必须释放连接后重新建立连接。

同步 SYN

1 位,为 1 时,表示这是一个连接请求或连接接受报文。

终止 FIN

1 位,为 1 时,表示要求释放 TCP 连接。

窗口大小 Window size

2 字节,用来让对方设置发送窗口的依据,单位时字节。

校验和 Checksum

伪首部 + 首部 + 数据的校验和,伪首部格式与 UDP 的伪首部相同。

紧急指针 Urgent pointer

2 字节,指出本报文段中紧急数据共有多少个字节,紧急数据放在数据的最前面。

选项 Options

长度可变,最长 40 字节。

最早定义的一种选项为最大报文段长度 (Maximum Segment Size, MSS),用以告知对方报文段中数据的最大长度,双方可使用不同的 MSS,缺省 MSS = 536 字节。

后续增加的选项有窗口扩大选项、时间戳选项、选择确认选项等。

填充字段

为了使整个首部长度为 4 字节的整数倍。

TCP可靠传输的实现


TCP 基于滑动窗口协议实现可靠传输和流量控制,滑动窗口以字节为单位。

以字节为单位的滑动窗口

发送窗口

没有收到对方应答的情况下,可以连续把窗口内的数据发送出去

窗口大小由对方发来的窗口大小、拥塞控制确定的,而不总是一样大。窗口会根据收到对方的 TCP 报文段头部中的确认号字段向前滑动

接收窗口

窗口内的数据是允许接收的。而窗口后沿以外是已正确接受并交付上层的数据。

TCP 要求接收方必须要有累计确认的功能,这样可减少传输开销。

发送缓存

用以暂时存放

  • 发送应用进程传送给 TCP,但仍未发出的数据
  • 已经发出,但仍未得到确认的数据

接受缓存

用以暂时存放

  • 按序到达的、但仍未被接收应用进程读取的数据
  • 未按序到达的数据

超时重传时间的选择

TCP 的可靠传输通过校验和+超时重传实现。TCP 每发送一个报文段,就会对该报文段设置一个计时器。若计时器设置的重传时间到,却没有收到确认,就需重传该报文段

超时时间的设置是一个复杂的问题,因为 IP 层提供数据报服务时,每个数据报所选择的路由都有可能发生变化,这就导致传输层的往返时间变化较大。

为此,TCP 采用一种自适应算法计算超时重传时间,即加权平均往返时间 RTTSRTT_S。当第一次测量到 RTTRTT 时,RTTSRTT_S 值就取为所测量到的 RTTRTT 样本值。但以后每测量到一个新的 RTTRTT 样本,就按以下公式重新计算一次 RTTSRTT_S

新的 RTTS=(1α)×(旧的RTTS)+α×(新的RTT样本)RTT_S = (1-\alpha) \times (旧的 RTT_S)+\alpha \times (新的 RTT 样本)

在以上公式中,0α<10\le \alpha \lt 1。若 α\alpha 很接近于零,标示新的 RTTSRTT_S 值与旧的 RTTSRTT_S 值相比变化不大,对新的 RTTRTT 样本影响也不大。若选择 α\alpha 接近于 11,则表示新的 RTTSRTT_S 值收新的 RTTRTT 样本的影响较大。已成为建立标准的 RFC 6298 推荐的 α\alpha 值为 1/8,即 0.125。

超时重传时间 (Retransmission Time-Out, RTO)

RTO 应该要略大于 RTTSRTT_S。RFC 6298 建议使用以下公式计算 RTO。

RTO=RTTS+4×RTTDRTO=RTT_S+4\times RTT_D

RTTDRTT_D 是 RTT 的偏差的加权平均值,它与 RTTSRTT_S 和新的 RTT 样本之差有关。RFC 6298 建议这样计算 RTTDRTT_D。当第一次测量时,RTTDRTT_D 值取为测量到的 RTT 样本值的一半。但以后的测量中,则使用以下公式计算加权平均的 RTTDRTT_D

新的RTTD=(1β)×(旧的RTTD)+β×RTTS新的RTT样本新的RTT_D=(1-\beta)\times (旧的 RTT_D)+\beta \times \mid RTT_S-新的 RTT 样本\mid

这里 β\beta 是个小于 1 的系数,它的推荐值是 1.4,即 0.25。

TCP 的流量控制


流量控制是让发送方的发送速率不要太快,使接收方来得及接收

利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制

持续计时器 (persistence timer)

TCP 为每一个连接设有一个持续计时器。只要一方收到对方的零窗口通知,就启动持续计时器

若持续计时器设置的时间到,就发送一个零窗口探测报文段,而对方在确认这个探测报文段时给出当前窗口值。若窗口值仍然为零,则收到这个报文段的一方就重新设置持续计时器。

窗口值不为零,则死锁的僵局就可以打破了。

传输效率

可以用不同的机制来控制 TCP 报文段的发送时机

机制一:缓存数据达到一定量就发送

TCP 维持一个变量,该变量等于最大报文段长度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去。

机制二:应用进程控制

发送方的应用进程指明要求发送报文段,即推送 (push) 操作

机制三:定时发送

发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入长度不超过 MSS 的报文段发送出去

TCP 的拥塞控制


某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就会变坏,从而产生拥塞。出现网络拥塞的条件如下:

对资源的需求>可用资源\sum 对资源的需求 \gt 可用资源

其中网络资源包括链路带宽、路由节点缓存及处理能力等。

拥塞的产生原因

网络拥塞往往是由多种因素引起的。如某路由器缓存容量太小,造成到达该节点的报文丢失。假设增加了缓存容量,报文可以在缓存中排队,但如果路由器的处理能力和出口链路带宽未增加,则报文排队时间过长,发送的主机将超时重发。因此简单扩大缓存并不能解决网络拥塞的问题,反而还会造成网络资源的严重浪费

网络拥塞会导致网络性能下降,具体表现为网络吞吐率下降、报文传输时延增大、丢包率增加以及用户端响应时间变长等。而且网络拥塞常常会趋于恶化。如一个路由器因缓存空间不足而丢弃了部分报文,发送端主机将超时重发,导致网络中被注入更多的报文,从而加剧了拥塞。

拥塞控制与流量控制

所谓拥塞控制,就是防止过多的数据注入到网络中使网络中的路由器或链路不至于过载。拥塞控制是一个全局性的过程,涉及所有的主机、路由器,以及与降低网络传输性能有关的所有因素。

相反的,流量控制往往是指点对点通信量的控制,是端到端的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便接收端来的及接收。

开环控制

开环控制是在设计网络时事先将有关发生拥塞的因素考虑周到力求网络在工作时不产生拥塞

闭环控制

闭环控制则是基于反馈环路的概念,主要有以下几种措施:

  • 监测网络系统以便检测到拥塞在何时、何处发生
  • 拥塞发生的信息传送到可采取行动的地方
  • 调整网络系统的运行以解决出现的问题

如何判断是否发生拥塞

拥塞发生时,路由器将丢弃分组。简单的主机端判断方式是,只要没有收到确认,就认为发生了拥塞

拥塞控制方法

RFC 2581 中定义了四种拥塞控制的方法,即:

  • 慢启动 (slow-start),又称为慢开始
  • 拥塞避免 (congestion avoidance)
  • 快重传 (fast retransmit)
  • 快恢复 (fast recovery)

慢启动/慢开始 (slow-start)

当主机在已建立的 TCP 连接上开始发送数据时,并不清楚网络当前的负荷情况。如果立即把大量数据注入网络,可能会导致拥塞。因此较好的方法是先探测一下,即由小到大逐渐增大注入到网络中的数据字节,也就是由小到大逐渐增大拥塞窗口数值。

工作过程

开始发送报文段时,设置拥塞窗口 cwnd = 1,即设置为一个最大报文段 MSS 的数值。之后每收到一个对新的报文段的确认后,将拥塞窗口加 1,即增加一个 MSS 的数值。

用这样的方法逐步增大发送端的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。

拥塞窗口 (congestion window, cwnd)

由发送方维持拥塞窗口,是一个状态变量。其大小取决于网络等于拥塞窗口,并且动态地在变化。

发送方让自己的发送窗口等于拥塞窗口,如再考虑到接收方的接收能力,则发送窗口可能小于拥塞窗口。只要没有出现拥塞,拥塞窗口就增大一些,以便把更多的分组发送出去。一旦出现拥塞,拥塞窗口就缩小一些,以减少注入到网络中的分组数。

传输轮次 transmission round

使用慢启动时,每经过一个传输轮次,拥塞窗口 cwnd 就会加倍。一次传输轮次所经历的时间就是往返时间 RTT。

这里强调把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。

慢启动门限 ssthresh

防止拥塞窗口 cwnd 增常过大而导致拥塞,就设置一个慢启动门限

cwnd < ssthresh 时,使用慢启动算法

cwnd > ssthresh 时,停止使用慢启动算法,改用拥塞避免算法

cwnd = ssthresh 时,可使用慢启动算法或拥塞避免算法

拥塞避免 (congestion avoidance)

拥塞避免就是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍,使拥塞窗口 cwnd 按线性规律缓慢增长。

无论在慢启动阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞,就要把慢启动门限 ssthresh 改为出现拥塞时的发送方窗口值的一半,但不能小于 2。然后把拥塞窗口 cwnd 重新设置为 1,执行慢启动算法

这样可迅速减少注入到网络中的分组数,使得发生拥塞的路由器有足够的时间把队列中积压的分组处理完毕。

乘法减小 multiplicative decrease

无论在慢启动阶段还是拥塞避免阶段,只要出现一次超时,就把慢启动门限 ssthresh 减小到拥塞窗口的一半

这样当网络频繁出现拥塞时,ssthresh 值下降得很快,以大大减少注入到网络中的分组数。

加法增大 additive increase

执行拥塞避免算法后,每经过一个往返时间 RTT,把拥塞窗口 cwnd 增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。

快重传 (fast retransmit)

如果发送方在超时时间内未收到确认报文,说明网络中发生了拥塞,此时发送方应该尽早减少窗口宽度。

每当接收方收到一个失序的报文段,就发出重复确认,以便使发送方及早知道有报文段没有到达接收方。

发送方只要连续收到三个重复确认,就应该立即重传对方尚未收到的报文段。

快恢复 (fast recovery)

发送端收到连续三个重复的确认时,进行乘法减小,把慢启动门限 ssthresh 减为当前拥塞窗口宽度的一半

接下来不执行慢启动算法,而是设置为慢启动门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法,使拥塞窗口缓慢地线性增大。

发送窗口的上限值

从流量控制角度考虑,发送窗口不能超过对方给出的接收窗口值

综合考虑流量控制和拥塞控制,发送方的发送窗口的上限值应当在接收方窗口 rwnd 和拥塞窗口 cwnd 两者中取较小的一个,即发送窗口的上限值 = min[rwnd, cwnd]

当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值。

当 rwnd > cwnd 时,则是网络的拥塞限制发送窗口的最大值。

TCP 的连接管理


TCP 传输连接有三个阶段,即连接建立、数据传送以及连接释放

连接建立的过程中需要解决的三个问题有:

  • 使每一方能够确知对方的存在
  • 允许双方协商一些参数,如最大报文段长度、最大窗口大小以及服务质量等。
  • 能够对传输实体资源进行分配,如缓存、连接表中的项目等。

TCP 连接的建立都是采用客户/服务器方式

客服 (client) 指的是主动发起连接建立的应用程序。而服务器 (server) 则指被动等待连接建立的应用程序。

TCP 的连接建立

TCP 连接的建立采用三次握手 (three-way handshake)

  • A 向 B 发出连接请求报文段,其首部中的 SYN = 1,并选择序号 seq = x,表明传送数据时的第一个数据字节的序号是 x。
  • B 收到连接请求后,如同意,则发回确认,其中 SYN = 1,ACK = 1,确认号 ack = x + 1,自己选择的序号seq = y。
  • A 收到后向 B 给出确认,其 ACK = 1,确认号 ack = y + 1。
  • B 收到后,TCP 连接成功建立。

为什么采用 3 次握手

采用 3 次握手是为了防止失效的连接请求在服务器端占用资源

假设客户端发出了连接请求,但该数据报在网络中某处滞留了。客户端等待超时后,重发连接请求,服务器响应,建立连接。滞留的连接请求又到了服务器端,如果采用 2 次握手,服务器将建立一个连接,分配资源,这会占用资源且长期存活。

3 次握手的安全问题

TCP SYN Flooding 攻击,即攻击者连续发送大量 SYN 报文,但却不对 SYN ACK 报文做出响应。这导致服务器内部数据结构满,无法响应正常用户的 TCP 连接请求。

此类攻击大多使用 IP 地址伪装,使得对攻击源的定位比较困难。

Linux 内核采用了 SYN_Cookies 机制应对这种攻击。其思路是,服务器返回 SYN ACK时,根据自身特有信息计算Cookies,作为 seq 返回给客户端,并在收到对方应答前,不为该连接分配数据结构。

TCP 的连接释放

报文段最大存活时间 (Maximum Segment Lifetime, MSL)

RFC 793 中建议 MSL = 2 分钟一般使用更小的值

关闭连接前,等待 2MSL 时间的原因是为了保证 A 发送的最后一个 ACK 报文段能够到达 B,同时防止已失效的连接请求报文段出现在本连接中

This post is licensed under CC BY 4.0 by the author.

网络层 Network Layer

应用层 Application Layer