计算机网络 运输层

本文将介绍计算机网络中的运输层。

一、运输层的功能

  • 网络层为两台主机之间提供通信服务(点到点);

    运输层为两台主机中的应用程序之间提供通信服务(端到端)

  • 运输层会对收到的报文进行差错检测

  • 具有复用分用的功能

  • 运输层向高层用户屏蔽了下层的细节,表现为一条端到端的逻辑通信信道

    • 当运输层使用面向连接的 TCP 协议时,运输层会向上层提供一个全双工的可靠信道
    • 当运输层使用无连接的 UDP 协议时,运输层会向上层提供一个不可靠的信道
  • 根据使用的协议不同,传输单位分别为 TCP 报文段和 UDP 用户数据报

二、端口

正如 IP 地址标识主机一样,端口用于标识主机上的应用程序。两个计算机中的应用程序要相互通信,不仅需要知道双方的 IP 地址,同样还要知道双方的端口号。

端口又被称为套接字、socket、插口,它由端口号和 IP 地址组合而成。

三、复用和分用

  • 复用:所有应用程序都可以将数据交给同一个运输层
  • 分用:运输层会在接收到数据后将其分别交付给对应的应用程序

三、UDP

UDP 在网络层的基础上增加了复用、分用、差错检测。

UDP 具有以下特点:

  • 无连接:在发送数据之前不需要建立连接,减少了开销和时延
  • 支持一对一、一对多、多对一和多对多通信
  • 不保证可靠:尽最大努力交付,不保证传输的可靠性
  • 面向报文:对于上下层的数据,UDP 不会对其进行划分或合并,而是原封不动地交付
  • 没有拥塞控制:UDP 会以恒定的速率发送数据,而不关心网络是否拥塞
  • 首部开销小

四、TCP

1. 特点

  • 面向连接:传送数据前,需要先建立连接,传送数据后,需要释放连接

  • 一对一:每条 TCP 连接有且只有两个端点

  • 可靠交付:通过 TCP 连接传送的数据可以保证无差错、不丢失、不重复、按序到达

  • 面向字节流:TCP 会将上层的报文视为一连串的无结构的字节流,根据网络情况将其划分或合并后传输

    如果数据过大,TCP 可能划分为多块再发送;

    如果数据过小,TCP 可能等待累积足够的量后再一起发送

  • 全双工通信:TCP 允许通信双方在任何时候都能发送数据

    连接的两端都设有发送缓存和接收缓存,用于临时存放双向通信的数据。

    • 在发送时,只需要将数据 “丢给” TCP 的缓存,TCP 会在合适的时候将数据发送出去
    • 在接收时,TCP 将收到的数据放入缓存,应用程序可以在合适的时候读取数据

2. 端口与连接

每条 TCP 连接有且只有两个端点,每个 TCP 连接唯一地被两个端口所确定。

3. TCP 报文段首部

TCP 传送的数据单元是报文段,一个报文段可以分为首部和数据两部分。

其中,首部包含:

  • 源端口、目的端口:各占两个字节

  • 序号 seq:在 TCP 连接中传送的字节流中,每一个字节都会按照顺序编号,报文段首部的序号位应填入报文段第一个字节的序号

    序号范围为 0 ~ 2^32 - 1

  • 确认号 ack:表示期望收到对方下一个报文段的序号值

    B 正确收到 A 发送的报文段,其 seq 为 501,数据长度是 200,这表明 B 正确收到了 501 ~ 700,因此应该发送 ack 值为 701 的确认报文段。

    ack 为 N 时表示:到 N-1 为止的所有数据都已正确收到

  • 数据偏移:指出数据起始处的位置的序号和报文段序号的差值,实际上是指出首部长度

  • 保留:保留位

  • 紧急 URG:该字段用于告诉系统该报文段中有紧急数据,应该尽快传送

  • 确定 ACK:置为 1 时,表示 ack 有效

  • 推送 PSH:置为 1 时,表示接收方应该尽快交还响应

  • 复位 RST:置为 1 时,表示 TCP 连接中出现严重差错,必须释放连接后重新建立

  • 同步 SYN:在连接建立时用于同步序号

  • 停止 FIN:用于释放连接

  • 窗口:表示接收方目前允许对方发送的数据量

  • 校验和

  • 紧急指针:当 “紧急 URG” 为 1 时才有意义,指向本报文段中的紧急数据

  • 选项:最大报文段长度、窗口扩大选项、时间戳、选择确认等信息

4. 可靠传输

(1) 停止等待协议

停止等待,就是发送完一个分组之后就停止发送,等待对方的确认,在收到确认后再继续发送。

  • 无差错时:

    • A 发送分组,等待确认
    • B 收到分组,发送确认
    • A 收到确认,继续发送
  • 发生差错时:

    • A 发送分组

    • B 接收到分组后发现其在传输过程中发生差错,丢弃后什么也不做

      在可靠传输的协议中,也可以在检测出有差错时发送 “否认报文”,这样做的好处是能够让发送方及早知道出现了差错,但由于这样做会使协议复杂化,现在实用的可靠传输协议都不使用这种 “否认报文” 了。

    • A 发送分组后超过一定时间没有收到确认,重新发送分组

      又被称为超时重传

    • B 收到分组,发送确认

    • A 收到确认,继续发送

  • 确认丢失:

    • A 发送分组,等待确认
    • B 收到分组,发送确认,确认丢失
    • A 发送分组后超过一定时间没有收到确认,重新发送分组
    • B 收到重复分组,丢弃,重传确认
    • A 收到确认,继续发送
  • 确认迟到:

    • A 发送分组,等待确认
    • B 收到分组,发送确认,确认迟到
    • A 发送分组后超过一定时间没有收到确认,重新发送分组
    • B 收到分组,发送确认
    • A 收到确认,继续发送
    • A 收到迟到的确认,收下但什么也不做

(2) 连续 ARQ 协议

连续 ARQ 协议会维护一个滑动窗口,滑动窗口内的分组将被 “连续发送”,等待对方的确认,移动滑动窗口继续发送。

接收方会采用 “累积确认” 的方式,只发送对按序到达的最后一个分组的确认,表示 “这个确认的分组之前的分组都已正确收到”

  • 全收到情况:
    • A 发送 1、2、3、4、5 分组
    • B 收到全部分组,发送 5 分组的确认
    • A 收到 5 分组的确认,移动滑动窗口,继续发送 6、7、8、9、10 分组
  • 半收到情况:
    • A 发送 1、2、3、4、5 分组
    • B 收到1、2、4、5分组,发送 2 分组的确认
    • A 收到 2 分组的确认,移动滑动窗口,继续发送 3、4、5、6、7 分组

(3) TCP 的可靠传输实现

TCP 的可靠传输类似连续 ARQ 协议,但是更加复杂。

TCP 双方各自会维护两个以字节为单位的窗口,分别叫做发送窗口和接收窗口。

假设有 A 和 B,讨论 A 作为发送方、B 作为接收方的情况(实际上 TCP 是全双工的,A 和 B 都同时是发送方和接收方)。

  • 发送窗口:

    A 会根据 B 的报文段中的窗口(决定窗口大小)和确认号(决定窗口位置)维护一个发送窗口,如图:

    • 对于发送窗口左侧的数据,它们已经确认发给接收方,因此会被丢弃
    • 对于发送窗口内的数据,每隔一段时间,发送方会将它们连续发送
    • 对于发送窗口右侧的数据,它们不允许被发送,因为接收方暂时无法接收它们
  • 接收窗口:

    B 会维护一个接收窗口,并将接收窗口的大小通知给 A。

    • 对于接收窗口左侧的数据,它们已经交付,因此会被丢弃
    • 对于接收窗口内的数据,接收方会接收数据并暂存在窗口中,一旦构成 “从最左侧开始的连线”,便可以发送按序到达的最后一个数据的确认,更新接收窗口
    • 对于接收窗口右侧的数据,不允许接收

5. 流量控制

(1) 什么是流量控制?

如果发送方将数据发送得过快,接收方可能来不及接收只能丢弃,导致这个过程中存在大量的无用流量。

(2) 流量控制的实现

接收方向发送方发送数据时,可以通过报文段首部中的窗口说明接收窗口的大小,从而让发送方调整发送窗口的大小。

6. 拥塞控制

(1) 什么是拥塞?

在计算机网络中的链路容量(即带宽)、交换节点中的缓存和处理机等,都是网络的资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就会变化,这种情况就是拥塞。

(2) 拥塞控制与流量控制

  • 拥塞控制:

    • 防止过多的数据注入到网络中,进而导致网络过载
    • 全局性的
  • 流量控制:

    • 防止发送过多数据而接收方来不及接收
    • 点对点的通信量的控制

(3) 拥塞窗口

TCP 的拥塞控制基于窗口,发送方将根据网络的拥塞程度维护一个 “拥塞窗口” 变量,它将影响发送窗口的大小。

发送窗口大小 = min(拥塞窗口, 接收方窗口)

(4) 拥塞窗口变化原则

  • 如果网络未发生堵塞,应该将拥塞窗口增加,以提升网络的利用率
  • 如果网络发生堵塞,应该将拥塞窗口减小,以缓解网络压力

(5) 对拥塞的判断

只要接收方的确认报文段超时,便可以认为网络出现了拥塞。

(6) 拥塞窗口增长的两个阶段

  • 慢开始阶段:

    • 如果一个 TCP 连接刚建立,此时并不清楚网络的情况,应该从小到大逐渐增大注入到网络中的数据量,即从小到大逐渐增大拥塞窗口

    • 此阶段拥塞窗口将成倍增长

  • 拥塞避免阶段:

    • 该阶段的目的是让拥塞窗口缓慢增大
    • 此阶段拥塞窗口将线性增长

(7) 避免拥塞误判

  • 快重传机制:该机制的目的是区分拥塞和报文段缺失

    • 接收方一旦接收到报文段,无论是否连续的,都应该立即发送目前已收到的最大报文段的确认
    • 当发送方一旦连续收到三个重复确认后,便可以确定出现报文段缺失而非网络拥塞,发送方便可以立即进行重传

  • 快恢复:一旦通过快重传机制确定是报文段缺失,发送方就可以快速增大拥塞窗口值

7. TCP 的连接建立

(1) 具体流程

TCP 建立连接的过程常被称为 三次握手,但它更准确的叫法是 三报文握手

three way (three message) handshake

– RFC 793

在 TCP 协议中,主动发起连接请求的一端称为客户端,被动接收连接请求的一端称为服务端。

具体连接流程如下:

  • 最初,服务端和客户端都会是 CLOSE(关闭) 状态

  • 服务端创建传输控制块 TCB,准备接收客户端的连接请求,进入 LISTEN(监听) 状态

  • 客户端创建传输控制块 TCB

  • 第一个报文:客户端向服务端发送 “客户端同步报文段”,其中 SYN = 1; ACK = 0; seq = x,进入 SYN-SENT(同步已发送) 状态

    • SYN = 1; ACK = 0 表示此报文段为 “客户端同步报文段”
    • seq = x 表示客户端发送的字节流的初始序号为 x
  • 第二个报文:服务端如果同意建立连接,应该向客户端发送 “服务端确认 + 同步报文段”,其中 SYN = 1; ACK = 1; seq = y; ack = x + 1,进入 SYN-RCVD(同步收到) 状态

    • SYN = 1; ACK = 1 表示此报文段为 “服务端确认 + 同步报文段”
    • seq = y 表示服务端发送的字节流的初始序号为 y
    • ack = x + 1 表示已经确认收到来自客户端的 “客户端同步报文段”
  • 第三个报文:客户端收到服务端的 “服务端确认 + 同步报文段” 后,应该发送 “客户端确认报文段”,其中 ACK = 1; seq = x + 1; ack = y + 1,进入 ESTABLISHED(已建立连接) 状态

    • ACK = 1 表示 ack 有效
    • seq = x + 1 表示客户端发送的字节流的初始序号为 x + 1
    • ack = y + 1 表示已经确认收到来自服务端的 “服务端确认 + 同步报文段”
  • 服务端收到 “客户端确认报文段”,进入 ESTABLISHED(已建立连接) 状态

  • 两者可以开始通信

(2) 三报文 = 四报文

第二个报文实际上是 “服务端确认报文段” 和 “服务端同步报文段” 的结合,为方便合并成一个。

1
2
3
4
三报文
= "客户端同步" + "服务端确认 + 服务端同步" + "客户端确认"
= "客户端同步" + "服务端确认" + "服务端同步" + "客户端确认"
= "四报文"

(3) 为什么需要那么多报文?

  • 第一个报文发送且被接收后,服务端可以得出结论:客户端发送能力正常
  • 第二个报文发送且被接收后,客户端可以得出结论:服务端接收能力、发送能力正常
  • 第三个报文发送且被接收后,服务端可以得出结论:客户段接收能力正常

三个报文是让双方都能确保对方可信的最小代价。

8. TCP 的连接释放

(1) 具体流程

TCP 连接释放的过程通常被称作 四次挥手

假设连接双方为 A 和 B,具体释放流程如下:

  • 最初,A 和 B 都处于 ESTABLISHED(已建立连接) 状态

  • 第一个报文:A 的上层应用程序要求 A 释放连接,A 发送 “连接释放报文段”,进入 FIN-WAIT-1(终止等待 1) 状态,等待 B 的确认

    “连接释放报文段” 中 FIN = 1; seq = u

    • FIN = 1 表示希望释放连接
    • seq = u 表示已传输数据的最后一个字节的序号为 u - 1
  • 第二个报文:B 收到 “连接释放报文段” 后,通知上层应用程序,发出 “确认报文段”,进入 CLOSE-WAIT(关闭等待) 状态

    “确认报文段” 中 ACK = 1; seq = v; ack = u + 1

    • ACK = 1 表示 ack 有效
    • seq = v 表示已传输数据的最后一个字节的序号为 v - 1
    • ack = u + 1 表示已经确认收到来自 A 的 “连接释放报文段”
  • TCP 连接进入半关闭状态,A 不再发送、B 不再接收、B 仍可发送、A 仍会接收

  • A 收到来自 B 的 “确认报文段”,进入 FIN-WAIT-2(终止等待 2) 状态,等待来自 B 的 “连接释放报文段”

  • 第三个报文:若 B 的上层应用程序已没有要发送的数据,它会要求 B 释放连接,B 发送 “连接释放报文段”,进入 LAST-ACK(最后确认) 状态,等待 A 的确认

    “连接释放报文段” 中 FIN = 1; ACK = 1; seq = w; ack = u + 1

    • FIN = 1 表示希望释放连接

    • ACK = 1 表示 ack 有效

    • seq = w 表示已传输数据的最后一个字节的序号为 w - 1

      w >= v,因为 B 的 CLOSE-WAIT ~ LAST-ACK 期间可能会继续发送一些数据

    • ack = u + 1 表示已经确认收到来自 A 的 “连接释放报文段”

      这里必须重复之前已经发送过的确认号

  • 第四个报文:A 收到 B 的 “连接释放报文段” 后,应该发送 “确认报文段”,进入 TIME-WAIT(时间等待) 状态,经过时间等待器 TIME-WAIT timer 后,A 进入 CLOSED 状态,撤销 TCB,结束 TCP 连接

    “确认报文段” 中 ACK = 1; seq = u + 1; ack = w + 1

    • ACK = 1 表示 ack 有效
    • seq = u + 1 表示最后一次传输就是第一个报文,其序列号是 u
    • ack = w + 1 表示已经确认收到来自 B 的 “连接释放报文段”
  • B 收到 A 的 “确认报文段” 后,进入 CLOSED 状态,撤销 TCB,结束 TCP 连接

(2) 三次挥手

如果关闭的被动方 没有数据要继续发送 && 开启 TCP 延迟确认机制,那么第二次、第三次挥手将会合并传输。

(3) 为什么有 TIME_WAIT ?

TIME_WAIT 出现在请求连接断开的发起方。

在 TCP 请求中,断开连接需要四次挥手,从而保证双方都希望关闭连接。

TIME_WAIT 发生在:A 发送关闭连接请求,接收到 B 的关闭连接确认,从 A 方出发的关闭流程结束;然后 A 接收到 B 的关闭连接请求,发送确认,并进入 TIME_WAIT 状态。

其存在是为了更加可靠的关闭连接,因为如果 A 的确认在请求中丢失,并且 A 直接关闭连接,则 B 将无法正常关闭连接。而如果 A 多等待一会儿,便可以在 B 重新尝试关闭连接时继续发出确认,帮助 B 关闭连接。

(4) 服务端大量 TIME_WAIT 的解决方案

  • 如果是服务端上的爬虫程序、反向代理程序大量发起请求导致的,应该考虑修改配置使得服务器能够快速回收和重用 TIME_WAIT 的请求
  • 如果是客户端往服务器的连接被服务器关闭导致的,应该避免使用短连接

参考

  • 《计算机网络》