loading...

35传输层

5.1 传输层基本概念

传输层传输基本单位为报文段/段,segment

  • UDP不允许拆分,整个报文就是一个报文段
  • TCP可以拆分,原始TCP报文会拆分成若干个报文段

传输层封装来自应用层的数据

  • 应用层的数据可能存在首部也可能不存在首部,由应用程序来决定
  • 不论来自应用层的PDU是否包含首部,传输层都会对其进行封装,并增加传输层的首部

传输层主要传输两种数据段:UDP和TCP

  • UDP和TCP的首部不相同

5.1.1 端口号

传输层根据端口号来区分数据来自/发往哪个进程

img

  • 两台主机的端口号时相互独立的
  • TCP、UDP协议的端口号也是相互独立的,即一台主机中的两个进程可以拥有端口号相同的TCP、UDP服务
  • 当两个进程之间想要通信时,需要指明:
    1. 使用哪种传输层协议,UDP or TCP
    2. 本进程绑定的端口号
    3. 对方的IP地址和端口号
  • 套接字,Socket:
    • 格式为<IP地址:端口号>,是一种数据结构,相当于一个"网络指针"。用于指明网络中一台主机的一个特定进程,该指明唯一
    • 同样的,也分为TCP套接字和UDP套接字
  • 假设进程1正在用TCP通信,进程2、6正在用TCP通信,进程3、7正在用UDP通信,进程4、8正在用UDP通信

端口号分类

端口号共16bit,分别为0 ~ 65535,主要分为两类:

  • 服务器使用的端口号
    • 熟知端口号:0 ~ 1023
    • 登记端口号:1024 ~ 49151
  • 客户端使用的端口号
    • 短暂端口号:49152 ~ 65535

0~1023为熟知端口号

  • 保留给各种系统服务进程或被熟知的重要应用程序使用,开发使用一般避免使用这些端口号
    • 使用这些熟知端口号的程序,一般系统会附带进去
  • 其余端口号实际开发时没有特别严格限制,只要本机没有其他京城占用该端口都可以使用
  • 端口号分类只是"建议标准",而不是"强制标准"
应用程序 服务 熟知端口号
FTP FTP服务 21
TELNET TELNET服务 23
SMTP SMTP服务 25
DNS DNS服务 53
TFTP TFTP服务 69
HTTP HTTP服务 80
SNMP SNMP服务 161

5.1.2 传输层功能

传输层主要实现端到端(进程到进程)的通信

复用和分用:

  • 复用:(从上到下)
    • 在发送数据时,同一台主机的多个进程可以使用同一个传输层协议
  • 分用:(从下到上)
    • 在接收数据时,传输层可以把数据正确交付给目的进程

差错检测:

  • TCP检测出错后会直接丢弃数据,并通知发送方重传
  • UDP检测出错后直接丢弃数据,且不通知发送方

向上层应用层提供服务:

  • 面向连接的、可靠的端到端传输服务TCP:
    • 确保数据正确/完整,但开销大、实时性较差
    • 例如,文件传输
  • 面向无连接的、不可靠的端到端传输服务UDP:
    • 数据可能出错/丢失,但速度快、开销小
    • 例如,视频通话,直播

有连接与无连接传输

img
有连接传输:

  • 传输前先打招呼,先确认对方已经准备好接收数据
  • 传输结束时也要告知对方已结束

无连接传输:

  • 不打招呼,直接把数据传给对方

可靠与不可靠传输

img
可靠传输:

  • 接收方使用“确认机制”让发送方知道哪些数据已被正确接收

不可靠传输:

  • 接收方无论收没收到数据、数据是否正确,都不给发送方反馈

5.2 UDP

img

  • UDP每次传输一个完整的报文,不支持报文自动拆分和重装
    • 如果传输层接收到来自应用层超出UDP协议长度上限的报文,会直接丢弃该报文
    • 如果一定要传输很大的UDP报文,只能在应用程序中自行编写拆分、重装功能代码以实现该功能
  • UDP是无连接的、不可靠的,不支持拥塞控制
    • UDP虽然是不可靠的,但是可靠性可以交给上层应用层处理
  • UDP支持一对一、一对多传输
    • 一对一传输,通过封装成单播IP数据报
    • 一对多传输,通过封装成广播/多播IP数据报。可用于直播

5.2.1 UDP数据报格式

img

  • UDP首部很小,只占8B
  • UDP长度字段占2B,以字节为单位,因此UDP数据部分长度理论最大长度为65535B
    • 但是由于UDP封装到IP数据报作为PDU需要同时满足IP数据报的最大数据长度
    • IP数据报数据部分理论最大长度为65515B,因此UDP长度最大限制在65515B。UDP数据部分长度限制在65507B

UDP传输示例

img

5.2.2 UDP检验

校验和:

  • 假设要发送的数据为32 bit,将原始数据按照16bit为一组,进行按位相加,结果进行逐位取反,取反16 bit结果作为检验和
  • 进行数据传输时,将原始数据和检验和一同发送
  • 当接收方收到这48 bit的数据后,也以16bit为一组,按位相加,如果得到的结果为全1,则说明传输无误;反之存在位错
  • 回卷:
    • 在进行按位相加的计算时,如果最高位发生了进位,则需要将该进位加到结果的最低位

UDP校验过程

发送方:
img

  • 发送方封装完UDP数据报准备向下传递前,先计算校验和
  • 计算校验和之前,先在该UDP数据报首部之前添加伪首部信息
    • 伪首部共12B,包括各4B的源IP地址、目的IP地址,1字节的0,1字节的无符号数17,2B的UDP长度
    • 伪首部的源IP、目的IP地址、UDP长度和UDP数据报的首部信息相同,UDP长度不包括伪首部
  • 将伪首部、首部、数据部分按照16bit为一组(不包括首部中的校验和字段),进行二进制加法(最高位进位需要回卷),结果逐位取反得到16bit校验和,填入UDP首部
  • 去掉伪首部,将UDP数据报交给下层网络层,封装成IP数据报

接收方:
img

  • 接收方收到层网络层传递过来的UDP数据报,处理UDP数据报前先进行校验
  • 校验同样先添加伪首部,从UDP数据报首部中复制信息
  • 将伪首部、首部、数据部分按照16bit为一组(其中也包括首部中的校验和字段),进行二进制加法(最高位进位需要回卷)
    • 如果加法结果为全1说明没有出错,接收方传输层接收该UDP数据报,并根据目的端口号,向应用层提交报文
    • 如果加法签过不全为1说明存在位错,传输层直接丢弃该UDP数据报

IP数据报的首部检验和计算也是和UDP检验和一样,对IP数据报首部以16bit为一组进行

IP数据报首部长度为4B = 32bit,按照16bit为一组的划分完全不存在问题

5.3 TCP

img

  • TCP支持报文自动拆分、重装,类同IP数据报的拆分和重组,因此可以直接传输长报文
  • TCP是有连接、可靠的,支持拥塞控制
  • TCP仅支持一对一传输,通信双方的传输层必循先建立连接

TCP协议三大阶段:

  • 建立连接,通过三个TCP段进行三次握手
  • 数据传输
  • 释放连接,通过四个TCP段进行四次挥手

img

  • 三次握手过程一定是客户端先向服务器发起,但四次挥手不强制要求客户端发起
    • 假如服务器已经传输完所有数据,可以先向客户端发起四次挥手

注:连接了一次TCP连接,可以连续传输多个报文(且支持全双工),不需要传输一个报文建立一次连接

  • 假设建立TCP连接时,双方协商MSS=1000B,Maximum Segment Size,最大段长
    img
    • TCP并不会强制要求每个TCP段均满载传输MSS大小的数据,只要求不超过MSS长度
    • 由于MSS的限制,需要传输很大的TCP报文会也会被拆分成多个TCP报文段进行传输
    • 不同于UDP面向报文,TCP是面向字节流的
      • TCP可以将原本多个报文的信息放入同一个TCP报文段中,只要不超过MSS即可
      • 无论一个TCP报文段中包含多少个报文,在TCP协议看累都是一连串字节流
    • TCP拆分后的分段可能乱序到达接收方,接收方的TCP协议会按序交给上层应用层对应的进程

5.3.1 TCP段格式

考研不需要记忆每个字段的位置,考试中会给出示意图

img

  • TCP首部比UDP更大,占20~60B
  • 源端口、目的端口字段各占16bit,范围为0 ~ 65535
  • 序号字段seq:
    • 标记了数据部分第一个字节在原始字节流中的位置,即拆分前在字节流的位置;重组过程利用该字段还原字节流的先后顺序
    • 一个TCP连接建立后进行数据传输,"起始序号"是发送方自己设置的,不一定从0开始
  • ACK字段和确认号相互关联,ACK占1bit,确认号占32 bit
    • 确认号记作ack或ack_seq,主要用于反馈,表示序号在该确认好之前的所有字节都已正确收到
    • 当ACK = 0时,ack_seq无效;当ACK = 1时,ack_seq有效
    • 只有建立连接时客户端向服务端发起的第一次"握手"TCP报文段的ACK = 0,其余包括后面几次握手,传输阶段,四次挥手的TCP报文段的ACK = 1
    • ack_seq也同样具有累计确认的特性

img

  • 数据偏移字段,Data Offset:
    • 占用4bit,表示TCP报文段首部的长度,类同IP数据报,以4B为单位
    • TCP首部中不会专门记录TCP报文段数据部分长度,也不会记录TCP报文段的总长度
      • IP数据报中记录了IP数据报的首部长度和总长度,两者相减可以得到IP数据报数据部分的长度,即TCP报文段的总长度
      • TCP报文段首部长度已知,两者相减,从而可得到TCP报文段的数据长度
  • 保留字段目前无实际含义,占用6bit,通常全部置0
  • 填充字段长度可变,主要作用是为了让整个TCP首部长度凑足4B的整数倍

img

这几个字段相对没这么重要

  • URG字段,紧急位:
    • 占1bit,当URG = 1时,紧急指针有效。此时表示该TCP数据段为紧急数据,应尽快插队发送
  • 紧急指针:
    • 紧急数据专用序号,和上面的序号字段功能类似
  • PSH字段:
    • 占1bit,当PSH = 1时,表示希望接收方尽快回复,主要用于交互式通信
  • RST字段:
    • 占1bit,当RST = 1时,表示出现严重差错,必须释放连接(如,主机崩溃)
    • 也可用于拒绝一个非法报文段(如,恶意黑客的攻击)

img

  • SYN字段,同步位:
    • 占1bit,当SYN = 1时,表示这是一个连接起高球或连接接收报文
    • 只有建立连接时第一次、第二次"握手"TCP报文段的SIN = 1,其余包括后面的握手,传输阶段,四次挥手的TCP报文段的SIN = 0
  • FIN字段,终止位:
    • 占1bit,当FIN = 1时,表示此报文段的发送方的数据已发送完毕,要求释放传输连接
    • 只有建立连接时第一次"握手"TCP报文段,以及释放连接时第三次"挥手"TCP报文段的FIN = 1,其余包括后面几次握手,传输阶段,四次挥手的TCP报文段的FIN = 0

img

  • 窗口字段:
    • 占16bit,表示接收窗口的大小,记作rwnd或rcvwnd
    • 表示从本报文段首部的ack_seq算起,接收方还能接收多少数据,以字节为单位
    • 该字段用于实现流量控制
  • 校验和:
    • 原理和UDP校验和类同,占16bit,计算校验和之前也需要添加12B伪首部
    • 与UDP不同的是,协议字段的17改为6,长度字段改为TCP段长度。注意,TCP段长度之前提到没有字段指明,需要另外通过IP数据报首部长度和总长度计算

img

  • 选项字段:
    • 该字段可为空, 也可非空
    • 建立TCP连接时,在TCP连接第一次、第二次"握手"TCP段,选项中协商MSS
    • MSS的值表示在接下来的数据传输中,一个TCP报文段最多携带多少数据(不包含首部,只考虑数据部分)
    • 通常MSS不会设置太大,以免在网络层被分片,通常为1KB

5.3.2 TCP连接管理

只有第一次握手ACK = 0,其余报文段ACK = 1

只有第一次握手、第二次握手SYN = 1,其余报文段SYN = 0

只有第一次挥手、第三次挥手FIN = 1,其余报文段FIN = 0

建立连接:三次握手

建立连接阶段SYN、ACK、seq、ack变化:
img

  • 只有握手1、握手2的SYN = 1
  • 只有握手1的ACK = 0
  • 握手1、握手2为SYN数据段,不允许携带数据,只包含TCP首部,但是需要消耗一个序号
    • 因此客户端向服务器发送数据从seq=667开始
  • 握手3数据段,客户端可以向服务端发送数据,也可以不发送数据
    img

建立连接阶段TCP状态变化:
img

  • 客户端向服务器发送请求前,TCP进程端口处于关闭,TCP处于CLOSE关闭状态
  • 接收方在接收到客户端发来的TCP报文段前可能关闭了TCP服务器端口,此时服务器TCP也处于CLOSE关闭状态
  • 客户端发出握手1时打开TCP客户端端口,客户端TCP状态转变为SYS-SENT同步已发送状态
  • 为了保证能接收到客户端建立连接,需要打开TCP服务器端口,将服务器TCP转为LISTEN监听状态,等待客户端建立连接
    • 如果服务器TCP处于CLOSE关闭状态,任何客户端发来的建立连接请求,其目的端口在服务器上找不到相关进程,自然会直接丢弃该TCP报文段
  • 处于监听状态的服务器收到来自客户端发来的握手1后,向客户端发出握手2,TCP状态变为SYS-PCVD同步已收到状态
  • 客户端收到来自服务器的握手2后,向服务器发出握手3,TCP状态改为ESTABLISHED已建立连接状态
  • 服务器收到来自客户端的握手3后,TCP状态也变为ESTABLISHED已建立连接状态,两者开始相互传输数据

建立连接阶段耗时分析:

  • 从发出握手1,到客户端进程可以发送数据,即从发出握手1到发送握手3之前,至少需要1RTT时间
  • 从发出握手1,到服务器进程可以发送数据,即从发出握手1到收到握手3,至少需要1.5RTT时间

释放连接:四次挥手

释放连接阶段SYN、ACK、seq、ack变化:
img

  • 只有挥手1、挥手3的FIN = 1
  • 挥手1、挥手3为FIN段,允许携带数据,但是现实场景中并不携带数据
    • 但是尽管不携带数据,依旧要消耗一个序号
  • 挥手2可以携带数据
  • 由于挥手3顺利接收后,客户端和服务器之间的连接已经断开,因此挥手4不可以携带数据

释放连接阶段TCP状态变化:
img

  • 释放连接前,客户端和服务器TCP进程端口肯定处于打开状态,TCP状态均处于ESTABLISHED已建立连接状态
  • 此时可由客户端发起释放连接,也可由服务器发起释放连接,发送挥手1的一方TCP进入FIN-WAIT1终止等待1状态,后续步骤以客户端发起为例
  • 服务器收到挥手1后,向客户端发送挥手2,TCP并进入CLOSE-WAIT关闭等待状态,挥手2中可以携带最后需要发送的数据
  • 客户端收到挥手2后,TCP进入FIN-WAIT2终止等待2状态
  • CLOSE-WAIT关闭等待状态主要携带在挥手2中最后数据的传输,传输结束后,再向客户端再次发送挥手3,TCP进入LAST-ACK最后确认状态
  • 客户端收到挥手3后,向服务器发送挥手4,TCP进入TIME-WAIT时间等待状态,并启动计时器,等待2MSL延时后关闭TCP客户端端口,TCP进入CLOSE连接关闭状态

释放连接阶段耗时分析(基于客户端发出释放连接):
img

  • 最长报文段寿命MSL,Maximum Segment Lifetime:
    • 由TCP协议规定的一个固定的时间长度
  • 客户进程收到挥手3后,立即进入TIME-WAIT时间等待状态,并启动TIME-WAIT计时器
    • 由于服务器不一定能顺利收到挥手4,服务器发出挥手3后没有收到来自客户端的确认会重发挥手3
      • 该重传只会进行一次,如果该FIN报文段依旧没有收到ACK,则服务器就直接放弃并关闭连接
    • 如果在计时器倒计时之内如果再次收到服务器发来的挥手3,就重置计时器
  • 如果服务器收到挥手1时,已经没有后续需要传送的数据,那么可以连续发出挥手2、挥手3
    • 那么服务器TCP处于TIME-WAIT时间等待状态的时间极短暂,接近为0
    • 同理客户端TCP处于FIN-WAIT1终止等待2状态的时间也极短暂,接近为0
    • 那么从客户端发出挥手1到客户端进入CLOSE状态至少需要1RTT + 2MSL
    • 从客户端发出挥手1到服务器进入CLOSE状态,至少需要1.5RTT

5.3.3 TCP可靠传输与流量控制

序号:

  • 进程在建立连接时确定起始序号,数据传输过程中,每个字节对应一个序号

确认机制:

  • 累计确认规则:
    • 如果收到ack_seq = n,说明序号在n之前的所有字节都已正确接收
  • 返回ACK的时机:推迟确认
    • TCP标准规定,推迟时间最多不能超过0.5s
    • 如果回复ACK的一方也有数据要传送给对方,立即返回ACK段,并携带需要传输的数据
    • 若连续收到两个长度为MSS的报文段,就应该立即返回ACK段
      • 在推迟确认过程中,如果连续收到两个长度为MSS的报文段,说明原报文分段前很大,检验出错重传代价很大,因此需要立即确认
  • 两种ACK段
    • 一般确认:一个ACK只有TCP首部,没有携带数据
    • 捎带确认:一个ACK段顺道携带了数据

重传机制:

  • 超时重传:
    • 每发送一个报文段,就设置一个计时器
    • 若计时器到期还没收到确认,就重传这个报文段并重置计时器
  • 快重传/冗余ACK
    • 让可能出错的报文段尽早重传,而不是非要等到超时再重传
    • 立即确认:
      • 与快重传配套使用的机制。每收到一个TCP报文段就立即返回一个ACK段
        • 即使收到一个失序报文段,也要立即返回ACK段
        • 失序报文段会导致发送方收到冗余的ACK
      • 当发送方连续收到三个确认号相同的冗余ACK时,就立即重传该确认号对应的报文段

流量控制:即滑动窗口机制

  • 接收方维持一个接收窗口,记为rcvwnd或rwnd
  • 发送方维持一个发送窗口

5.3.4 TCP传输底层原理

Socket套接字对象:

  • 套接字对象是一个数据结构,其中包含本机进程端口号,以及接收方的套接字<ip:端口>,其中的内容需要应用程序自行填入
  • 套接字对象中除了本机以及目的套接字外,还存放发送缓冲区和接收缓冲区
    • 发送缓、接收冲区本质上是,操作系统为本次TCP连接,所分配的char[]数组
    • 该缓冲区长度可由操作系统默认分配或者应用程序在系统调用socket()时指明(但操作系统有最大最小限制)
      • 套接字对象创建后,也可通过系统调用setsockopt()对缓冲区大小进行调整
      • 发送缓冲区和接收缓冲区大小可以不等长
  • 发送/接收窗口大小不得超过发送/接收缓冲区大小,发送窗口大小也不得超过对方接收窗口大小

系统调用send()工作过程:

  • 应用程序进行系统调用send()时,需要指明套接字对象、用户区中发送数据的数组名(指针)以及长度

    ssize_t send(int sockfd, const void *buf, size_t len, int flags)

    • 调用send(),会将用户区的数据复制到对应套接字的发送缓冲区

系统调用recv()工作过程:

  • 应用程序进行系统调用recv()时,也需要指明套接字对象、用户区存储接收数据的数组名(指针)以及长度

    ssize_t recv(int sockfd, void *buf, size_t len, int flags)

    • 调用recv(),会将套接字对象的接收缓冲区的数据复制到用户区中

假设握手1为seq=599;握手2为ack=600、seq=199、rwnd=8;握手3为ack=200、seq=600、rwnd=10且不携带数据

  • TCP连接发起方:
    • 用户主机待发送数据为100B,设发送char[]数组序号为0 ~ 99,则对应用户端发送数据序号601 ~ 699
    • 由于接收方的rwnd=8,因此用户主机发送窗口大小最大也为8
      • TCP三次握手后,第一次发送过程,最大可发送 0 ~ 7序号,即接收方顺利收到整个发送窗口的数据后,ack = 608
        • TCP协议会将0 ~ 7序号的数据封装成TCP报文段,在传输层进行传输
  • TCP连接接收方:
    • 服务器主机发送数据为50B,设发送char[]数组序号为0 ~ 49,则对应用户端发送数据序号201 ~ 250
    • 由于接收方的rwnd=10,小于服务器主机发送窗口大小,服务器主机可以直接按最大发送窗口8发送
      • TCP三次握手后,第一次发送过程,最大可发送 0 ~ 7序号,即接收方顺利收到整个发送窗口的数据后,ack = 208
      • TCP协议会将0 ~ 7序号的数据封装成TCP报文段,在传输层进行传输

补充:
img

  • 同一台主机中的一个进程可使用一个端口建立多个TCP连接
  • 不同的TCP连接使用不同的套接字对象,每个TCP连接仅支持一对一全双工通信

TCP连接建立

img

  • 客户主机166.1.1.1与服务器主机233.0.0.1进行TCP通信,数据在应用层进行分析处理
    • 客户进程为进程A,端口号为1111;服务器进程为进程B,端口号为996
    • 处理这些数据的应用程序以及所需资源位于操作系统内存的用户区,而TCP协议所使用的资源位于操作系统主存的内核区
  • 假设进程A要向进程B发送100B数据,进程B要向进程A发送50B数据
    • 发送的数据本质上是长度为100B的char[]数组,数组内为需要发送的数据(或者直接理解为连续输入的字节流)
    • 接收的一方在应用层需要预留一段空间用于分析这些接收数据
      • 本质上也是一个char[]数组,数组长度由用户自行决定,如果长度不足需要进行动态扩容
  • 应用层准备好的数据交给传输层,需要进行系统调用socket()申请一个Socket套接字对象
    • 由于用户区无法直接访问内核区的资源,必须通过系统调用才能访问
  • 应用层准备好套接字对象后,进行系统调用send(),让操作系统的TCP协议根据套接字对象内的信息,对目标进程发起TCP连接,即发送握手1
  • 接收方接收握手1后,操作系统也会生成一个套接字对象,包含发送方的套接字以及本机进程端口号信息
  • 后续的TCP报文段交换过程,就通过这两个套接字对象进行

TCP通信过程——调整接收窗口大小

img

  • 握手3过程并不携带数据,因此TCP建立阶段未传输任何数据
  • 客户端向服务器发送TCP报文段ack=200,seq=600,rwnd=10,数据长度为3B
    • 客户端应用程序此时进行了系统调用send(),先从用户区将复制3B到发送缓冲区,发送缓冲区再取出交给TCP协议封装为TCP报文段进行发送
    • 客户端发送仅发送了3B,发送缓冲区还剩余7B,等待服务器发来ACK
  • 服务器收到3B数据后,如果校验通过,更新接收窗口大小为5,返回ACK,ack = 603,不携带数据
    • 传输过程中seq不会像建立连接阶段额外占用一个seq序号
    • 服务器执行应用程序此时进行了系统调用recv(),从接收缓冲区取出3B封装上交给应用层
    • 服务器接收窗口=接收缓冲区大小=8B,接收到3B,剩余5B,因此要将接收窗口最大调整为5B
      • 如果服务器应用程序未进行系统调用recv(),接收缓冲区内的数据会一直占用,直至释放连接
  • 客户端收到服务器的ACK,ack = 603,如果校验通过,将发送缓冲区的确认接收的数据移出,即移动滑动窗口(实际上是调整缓冲区指针)
    • 如果客户端一直未收到ACK,且此时发送缓冲区已满,应用程序进行系统调用send(),会引起报错或阻塞,防止发送缓冲区溢出
    • 客户端收到ACK确认后,会根据收到的rwnd调整自己的发送窗口大小
      • 客户端应用程序进行系统调用send(),无论发送缓冲区还剩多少空间,是否已满,都不会修改客户端发送窗口大小
    • 假如由于发送窗口大小限制,客户端应用程序进行系统调用send()所要求发送的数据未能一次性封装成一个TCP报文发送
      • 假设收到ACK后发送窗口大小扩大,剩余的部分可和下一次进行系统调用send()一同封装成TCP报文段发送
      • 如果客户端后续未进行新的系统调用send(),TCP协议会自动尝试发送剩余的部分,该过程不需要再执行新的系统调用send()来干预

TCP通信过程——接收方累计确认

img

  • 客户端向服务器连续发送两个TCP报文段,长度分别为2B,seq分别为603、605
    • 客户端发送缓冲区还剩6B,等待服务器发来ACK
  • 服务器收到多个TCP报文后,如果校验通过,只返回一个ACK,ack = 607,且不携带数据,并更新rwnd = 1
    • 服务器收到4B的数据,接收缓冲区剩余4B,接收窗口最大调整为4B
    • 累计确认,只返回最后的seq的ACK
  • 客户端收到服务器的ACK后,如果校验通过,根据rwnd调整发送窗口大小,只发送1B数据
    • 客户端收到ACK,发送缓冲区释放已确认的空间,发送缓冲区剩余9B,等待服务器发送ACK
  • 服务器再次收到TCP报文,如果校验通过,返回ACK,且不携带数据,由于缓冲区清空,更新rwnd = 8
    • 服务器需要尽快将数据按需交付给应用层,释放缓冲区空间,以扩大接收窗口大小

TCP通信过程——接收方捎带确认

img

  • 客户端向服务器发送包含数据2B的TCP报文段,seq=608
    • 客户端发送缓冲区剩余8B,等待服务器发来ACK
  • 服务器收到2B数据后,如果校验通过,返回ACK,ack = 610,且捎带5B的数据,更新rwnd = 6
    • 如果服务器也有数据要发送,则收到一个TCP报文则立即回复ACK,并在该ACK报文中携带需要发送
    • 服务器接收方缓冲区剩余6B,调整接收窗口rwnd = 6
  • 客户端收到ACK后,如果检验通过,则顺带也接受了来自服务器发送的的5B数据,返回ACK,ack = 205,更新rwnd = 5
    • 和服务器一样,进行捎带确认,收到TCP报文立即回复
    • 只要双方都还有数据要发送,那么捎带确认就是一直轮流下去,直至收到一个不带数据的ACK
  • 客户端、服务器如果没有按序收到数据,那么接收窗口内收到的乱序数据不能被交付到应用层
  • 接收缓冲区的数据通常通过系统调用recv()接收,一般不会自动交付给应用层,以保证数据按序交付

TCP通信过程——超时重传机制

客户端发送的TCP报文段丢失
img

  • 客户端发送TCP报文后,会设置一个超时重传计时器,如果在限定时间内未收到ACK会进行重传
  • 重传过程,会将之前发送缓冲区未确认的数据再次封装成一个TCP报文,再次发送,ack,seq保持不变,并重置计时器
  • 客户端如果在限定时间收到了ACK,则将接受的数据移出发送缓冲区,进行后续的发送

服务器返回的ACK丢失
img

  • 客户端发送TCP报文后,设置一个超时重传计时器
  • 服务器收到TCP报文,检验通过,发送ACK,但是传输过程中发生了丢失
  • 计时器超时,客户端进行重传
  • 服务器收到TCP报文,检验通过,但是发现该TCP报文已经接收,直接丢弃,并立即返回ACK
  • 如果这次客户端顺利收到ACK,则确认发送;反之,放弃该数据的发送

TCP通信过程——快重传机制、立即确认机制

img

  • 由于之前的延迟确认机制,在该期间内可能连续收到多个TCP报文段,但是其中有报文丢失,假设第二个报文丢失
    • 服务器只能返回按序接收的第一个报文,其余报文虽然不是按序接收,但是保留在接收窗口内
    • 接收窗口需要相应的调整,并保留出已接收的空间
    • 对于用户端,由于第二个报文以及后续报文均超时,需要进行重传
    • 除第二个报文外,实际其余报文都已顺利接收,但是延迟确认机制导致其必须重发
  • 引入快重传机制,每接收一个TCP报文就回复一个ACK
    • 由于第二个报文丢失,客户端会收到三个相同的ack的冗余ACK,此时客户端应立即重发ack对应的报文段,以避免超时等待继续无效重传

在实际应用场景中,由于TCP报文段长度远大于不携带数据的ACK报文段长度,无效重传造成的浪费远大于冗余ACK的影响

5.3.5 TCP拥塞控制

拥塞控制算法:

  • 慢开始算法:
    • 适用于严重拥塞场景
    • cwnd拥塞窗口大小值从1开始,没收到一个ACK就让cwnd + 1
  • 拥塞避免算法:
    • 在未拥塞状态,提前动态调整cwnd拥塞窗口大小
    • 在一个RTT内,及时收到多个ACK,也只能让cwnd + 1
  • 快恢复算法:
    • 对于立即确认、快重传机制,发生快重传现象时
    • 一旦发生快重传,将阈值、cwnd都设置为当前cwnd的一般,然后切换到拥塞避免算法

拥塞控制与流量控制对比:

  • 拥塞控制:
    • 控制端到端的数据发送量,是对局部网络拥塞情况的调整
  • 流量控制:
    • 控制整个网络中每台主机的数据发送量,降低路由器负载,是对全局网络拥塞情况的调整

网络中的拥塞状况反映在发出的报文未能按时收到ACK,即丢包现象

  • 网络不拥塞:
    • 发出的每个报文段都能顺利收到ACK
  • 网络有点拥塞:
    • 如果使用立即重传机制,发送方收到冗余ACK引发快重传
  • 网络严重拥塞:
    • 如果发出的报文未能按时收到ACK,引发超时重传

当网络发生拥塞时,需要限制网络中各台主机的数据发送量

  • 主机发送窗口大小上限 = min

通常考研题目,会围绕以下特点:

  • 通常只涉及单项传输,即TCP连接双方只有一方发送数据
  • 通常默认每个TCP报文段都已最大段长MSS满载数据
  • 拥塞窗口的大小常以MSS的倍数作为单位
  • 接收方受到报文段,会立即确认;如果受到冗余ACK,会进行快重传
  • 一般假设接收方接收窗口足够大,发送窗口大小只受拥塞窗口限制

慢开始算法、拥塞避免算法

img

  • 横轴为传输轮次,即时间,以RTT为单位;纵轴表示拥塞窗口大小cwnd,以MSS为单位
  • 慢开始门限ssthresh:

    在考研题目中一般称作拥塞控制阈值

    • 当实际的拥塞窗口大小小于ssthresh时,拥塞窗口大小增长幅度快;当拥塞窗口大于等于ssthresh时,拥塞窗口增长幅度慢
    • 即拥塞窗口大小增长速度变化分界线

拥塞窗口变化过程:
img

  • cwnd初值从1开始,ssthresh初值为16,因此一开始只能发送一个MSS长度的TCP报文段
  • cwnd小于ssthresh时,使用慢开始算法,每收到一个ACK,拥塞窗口+1,那么相应的发送窗口大小也+1
    • 一开始只收到一个ACK,因此加1;后续由于发送窗口的增大,由于立即确认机制,每次收到的ACK都加倍,也导致发送窗口大小也加倍
  • 当拥塞窗口大小达到ssthresh后,切换为拥塞避免算法,每个RTT/每轮接收内,即使收到多个ACK,至多让cwnd+1
  • 拥塞避免算法,持续增长拥塞窗口大小,直到在某RTT/某轮次,出现了超时重传,说明网络中已经存在严重拥塞现象,需要迅速缩小拥塞窗口
    • 出现严重拥塞现象,立即切换为慢开始算法,cwnd回到1,同时此时调整阈值,ssthresh = 拥塞时cwnd / 2
    • 每次发生严重拥塞就减半,但是ssthresh不能小于2
  • 由于出现的是超时重传,即使用延迟确认,一旦出现严重拥塞就将cwnd变为1
  • 如果慢开始算法,让拥塞窗口进行倍增后超过ssthresh阈值时,本轮次拥塞窗口只能增长到ssthresh阈值,并切换为拥塞避免算法

快恢复算法

img

  • 横轴为传输轮次,即时间,以RTT为单位;纵轴表示拥塞窗口大小cwnd,以MSS为单位

拥塞窗口变化过程:
img

  • 在发生严重拥塞前,增长过程类延迟确认
  • 发生拥塞后,出现了冗余ACK,说明网络中有点拥塞
    • 立即切换为拥塞退避算法,ssthresh = cwnd = 拥塞时cwnd / 2
    • 那么后续都一旦发生冗余ACK,都会将ssthresh、cwnd减半,但是ssthresh不会小于2,不会再采用慢开始算法
  • 在TCP早期版本,Tahoe版本中,当出现了冗余ACK时,会切换为慢开始算法,类同延迟确认过程,目前已启用
    • 现在新版TCP,发生冗余ACK,一律切换为拥塞退避
posted @ 2024-11-04 00:16  GK_Jerry  阅读(104)  评论(0)    收藏  举报