传输层

传输层

概述

多路复用&多路分解

image-20210402201243799

传输层协议

UDP 协议

image-20210402204459854

TCP 协议

基本概念

  • 概念:面向连接(虚连接)的传输层协议

  • 功能:

  • 特点:

  • 报文格式:

    • 源端口号 目的端口号(2B)

    • 序号(4B):每次发送的报文段中 ==第一个字节的编号== 就是序号

      注:初始序号通常随机:(1) 避免分组重排序:防止滞留在网络中的分组在某个时刻到达导致传递不必要的信息 (2) 保证网络安全

    • 确认号(4B):每次确认报文段中希望得到的 ==下一个字节的编号== (序号 + 数据长度 = 确认号)

    • 首部长度(4bit):单位 ==4B==

    • 窗口大小(2B):接收方将当前 缓存空间 中的 窗口大小 值放入 确认报文 中返回给发送方

      注:发送方收到的窗口大小的值 = min{接收窗口大小,拥塞窗口大小}

    • 控制位:

      • URG:(1) URG = 1 传输的数据是紧急数据时 (2) 紧急传输的数据不需要在 缓存空间 中排队
      • ACK:(1) ACK = 1 连接建立过程中服务端回复确认报文段时 (2) 连接建立后所有报文段的 ACK 必须被置为 1 (无论报文段时确认报文段还是发送报文段)
      • PSH:不需要在接收窗口中等待应用层的处理,直接交付给应用层处理
      • RST:连接出现严重错误,需要重新建立连接
      • SYN:(1) SYN = 1 客户端请求建立连接 & 服务器端同意建立连接
      • FIN:(1) FIN = 1 客户端请求结束连接 & 服务器端同意结束连接
  • 图示

image-20210403150832072

功能

连接管理
  • 概念:连接建立 + 连接维护 + 连接释放

  • 前提:TCP 是全双工通信 -> ==有两条通信信道==

  • 过程:

    • 连接建立:服务器长期处于监听状态

      1. 客户端发送 SYN = 1

        • 客户端进入 ==SYN_SENT 状态==
        • 等待服务器响应;如果超时则利用重传机制,重传 SYN = 1
        • 不携带任何数据,但是消耗序号
      2. 服务端响应 ACK = 1和 SYN = 1

    1. 客户端响应 ACK = 1
      • 客户端进入 ==ESTABLISHED 状态==
      • 可以携带数据发送
      • 开始分配 ==TCP 缓存== 和 ==变量== 4. 服务器端接收到响应进入 ==ESTABLISHED 状态==
* 细节问题

    1. seq = x:x 的值是随机分配的

    2. 为什么是三次握手,不是两次,四次或者更多次

        * 非本质回答:资源浪费

            客户端发送的连接建立请求可能在网络中滞留,导致延时到达服务器;

            客户端选择重传连接建立请求,然后传输数据,最后关闭连接;

            但是,之前延时到达的服务器的连接请求在关闭连接后到达了;

            这时,服务器端就会认为客户端又开始请求建立连接了,就会发送响应 SYN = 1 ACK = 1;

            如果是两次握手,导致连接建立,但是客户端并不想要建立连接,所以就不会回复,导致服务器端会长时间的等待直到超时,浪费资源

            如果是三次握手,那么服务器端就会因为客户端不回复知道他不想建立连接,就不会一直等待

        * 本质回答:==三次握手是理论保证可靠传输的最小值==

            一次握手:客户端发送请求,显然他根本不知道服务器到底接收到没有,所以需要等待服务器的回复 -> 一次握手显然不可靠

            两次握手:服务器端回复了客户端,那么服务器端也不清楚客户端到底接收到没有,所以需要再次等待客户端的回复-> 两次握手显然也不太可靠

            三次握手:客户端回复了服务器,已经基本确认两端都是可以连通的,可靠连接可以基本建立了

            四次握手:客户端也不清楚服务器端是否接收到自己的再次回复,所以再次等待服务器端的回复 -> 显然相对于三次可靠性更高了

            ....

            实际上,我们可以设计非常多次的握手来保证可靠传输,但是三次就是理论上保证可靠传输的最小值

* 连接断开:客户端数据发送完毕

    1. 客户端发送 FIN = 1 

        * 客户端进入 ==FIN-WAIT1状态==
        * 客户端等待服务器的响应;超时则利用重传机制
        * 不携带任何数据,消耗序号

    2. 服务端响应客户端 ACK = 1

        * 服务器端进入 ==CLOSE-WAIT 状态==
        * 服务器允许客户端释放连接 -> <a style="color:red;">释放客户端到服务器端的单向连接</a>
        * 客户端连接释放之后不可以再发送数据;服务器端仍然可以发送数据
        * 不携带任何数据,消耗序号

        <a style="color:red;">上述两次握手经过后 TCP 连接处于半关闭状态,这期间服务器端仍然可能向客户端发送数据</a>

    3. 服务端发送 FIN = 1

        * 服务器端进入 ==LAST-ACK状态==

        * 服务器不再向客户端发送数据后准备关闭连接
        * 服务器端等待客户端响应;超时则利用重传机制

    4. 客户端响应 ACK = 1

        * 服务器端进入 ==CLOSED状态==,客户端进入 ==TIME-WAIT状态==
        * 服务器端到客户端的连接还没有释放

    5. 客户端等待时间结束(2MSL)

        * 客户端进入 ==CLOSED状态==
        * 服务器端到客户端的连接彻底释放

    6. TCP 连接关闭完成

* 细节问题

    1. 为什么是四次挥手不是三次挥手?
        * 连接建立实际上也可以看做四次握手,而是三次握手的原因,是因为 ACK + SYN 一起发送简化了过程
        * 连接关闭时两者不可以一起发送,因为确认客户端到服务器端的连接关闭后,服务器端到客户端的连接并不会立刻关闭 -> 造成四次挥手
    2. 为什么最后客户端需要等待一段时间之后才能关闭呢?
        * 因为客户端响应的 ACK 很有可能产生丢失,导致服务器端到客户端连接并没有关闭,如果此时客户端先行关闭,会导致服务器挂起
        * 所以客户端需要等待服务器先行关闭,自己再关闭,最后服务器端到客户端的连接才算彻底释放 -> TCP 关闭完成
    3. 为什么一定要等待 2MSL 长度的时间呢?
        * 报文段最长存活时间是 MSL
        * ?
    4. 如果服务器第一次响应客户端的报文段丢失,会出现什么情况呢?
  • 图示:

    • 连接建立

    image-20210419171811788

    • 连接断开

    image-20210419193439634

  • SYN 洪泛攻击

    • 概念:ddos 攻击的一种
    • 过程:
      • 攻击者发送 SYN = 1 的第一个报文段
      • 服务端接收第一个报文段后回复给攻击者相应的确认报文段
      • 攻击者此时不再回复服务器
      • 服务器没有接收到回复会不断地发送确认报文段,希望得到攻击者的响应
      • 服务器由于得不到攻击者的响应,连接挂起,最后因为超时而断开连接
    • 后果:消耗 CPU 和 内存,导致服务器宕机
可靠传输

4b3c32a16c1b3d029aa3199f0ca1413b.png

基本机制
GBN 协议
  • 内容:

    • 发送方拥有发送窗口:(1) 已经发送但未确认的分组 (2) 即将要发送的分组

      注:定义:(1) 最早已发送未确认的分组序号为 base (2) 即将要发送的第一个分组序号为 nextseqnum (3) 窗口长度为 N

    • 发送方 已发送但未确认的分组数量 不可以超过当前滑动窗口的大小 (nextseqnum - base ≤ N)

    • 发送方接收到 确认序号 ≥ base 的确认分组时就会将窗口向前移动 确认序号 - base + 1 个位置 (滑动窗口的含义)

      注:GBN 协议采用 累积确认

    • 发送方如果在规定时间没有接收到相应的确认分组就会 重传所有已发送但未确认的分组 (就是后退 N 步的含义)

    • 接收方 仅接收按序到来的分组 而对于乱序到来的分组直接抛弃并且发送 上次发送的确认分组

      注:接收方 丢弃所有乱序达到分组 不会进行缓存

  • 累积确认(Cumulative Acknowlegment):

    • 内容:
      • 接收方连续按序接收到 多个分组仅返回一个确认分组 用于通知发送方已正确接收到所有分组 (确认分组的序号是最后一个按序到达的分组的序号)
      • 发送方接收到确认分组后就认为该 确认序号之前 所有没有被确认的分组都已经被正确接收 (所以滑动窗口会向前移动多个位置)
    • 累积确认的目的就是为了加速传输的过程,没有必要对每个分组都进行确认
  • 优点:实现简单 -> 接收方不需要缓存任何乱序到的分组

  • 缺点:后退 N 步造成很多无意义的重传:即使正确到达接收方的分组也可能因为乱序而不被接收,所以发送方只能够重发所有未被确认分组

  • 窗口大小:$1 < W_t < 2^n - 1$ ($n$ 是比特的个数)

  • 细节:

SR 协议
TCP 协议
  • 核心:GBN 协议 + SR 协议的 混合体 :采用 累积确认 + 选择重传

  • 累积确认 + 选择重传:接收方会缓存乱序到达的报文段但是 不会对乱序到达的报文段返回相应的确认,而是持续的返回期望接收到报文段的确认 (累积确认)

  • 面向字节:(1) 应用层交付的每个报文中的比特都被隐式地 编号 (2) 每次将一定数量的字节封装成报文段发送

    • 报文段序号(seq):报文段中第一个字节的编号
    • 确认报文段确认号(ack):期望收到的下一个字节的序号 seq + data.length
  • 发送缓存:发送窗口(已发送未被确认字节 + 即将发送的字节) + 应用层交付的但还不可以被发送的字节

  • 接收缓存:接收窗口(已收到的字节 + 仍未收到的字节) + 已经确认但未被应用层使用的字节

  • 通信过程:

流量控制
  • 原因:发送方发送的速率太快导致 接收方来不及接收处理 (局部性造成的)

  • 核心:滑动窗口机制

  • 内容:

  • 问题:$rwnd = 0$ 时发送方就会停止发送数据,但是接收方处理完数据之后并不会通知发送方,那么发送方怎么才能够得知接收方已经处理完数据了呢?

    • 核心:设置定时器

    • 方式:定时器倒计时结束后发送方发送 不带数据的报文段

      (1) 如果接确认报文段的窗口大小字段为 0 则接收方没有处理完

      (2) 如果接确认报文段的窗口大小字段不为 0 则接收方已经处理完成:发送方可以重新开始发送数据了

  • 细节:窗口大小字段取决于 接收窗口大小 和 拥塞窗口大小 两个因素

拥塞控制
  • 概念:网络中的各个发送方的发送速率太快导致 路由器来不及接收处理 (全局性 造成的)

  • 核心:慢开始 + 拥塞避免 + 快重传 + 快恢复 (慢开始和拥塞避免是 TCP 拥塞控制的 强制部分)

  • 前提:发送方维护拥塞窗口大小变量 cwnd

  • 内容:

    • 慢开始:“探测”当前网络的通信状况

      (1) $cwnd_0 = 1$:发送方在刚开始仅发送一个 报文段 (字节数量 = MSS) 用于试探网络当前的堵塞状况

      (2) 发送方成功接收到接收方返回的确认报文后更新 $cwnd$ 的值

      (3) $cwnd = 2^{n-1}$ ($n$ 是发送的轮次):发送方发送的报文段数量开始成 指数级增长

      (4) 发送方结束指数级增长的情况:1) 进入拥塞避免状态 2) 进入快重传机制 3) 出现网络拥塞

      (5) 出现网络拥塞之后:使 $ssthresh$ = $cwnd / 2$ 再将 $cwnd$ 的值重新置为 $1$ 重新开始 慢启动

    • 拥塞避免:$cwnd = ssthresh$ 时发送方结束指数增长开始线性增长:经过每个轮次之后 $cwnd++$

    • 快重传:发送方在接收到 $3个ACK$ 之后重传当前发送的报文段并且进入 快恢复

      注:快重传通常伴随快恢复

    • 快恢复:(1) 使 $ssthresh$ = $cwnd / 2$ 再将 $cwnd$ 的值重新置为 $ssthresh$ (2) 每经过一个轮次 $cwnd++$

  • 细节:窗口字段大小 = $min(rwnd, cwnd)$

image-20210405193342652

image-20210405194244655

Author: Fuyusakaiori
Link: http://example.com/2021/09/09/network/传输层/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.