### 传输层有两个协议
- TCP Transmission Control Protocol 传输控制协议
- UDP User Datagram Protocol 用户数据报协议
<img width="1167" alt="image" src="https://user-images.githubusercontent.com/39154923/137278301-c1a3560e-ac96-45b1-96e9-88a374efd267.png">

### UDP
- 无连接，减少了建立和释放链接的开销
- 尽最大能力交付，不保证可靠传输
- 首部较小，UDP首部只有8个字节
![image](https://user-images.githubusercontent.com/39154923/137895070-81ec320b-4196-4766-9438-2f8166e7712c.png)
- UDP长度：首部的长度 + 数据部分的长度
- UDP检验和：计算的是伪首部（12字节） + 首部（8字节）  + 数据
- 伪首部：仅在计算检验和时起作用，并不会传递给网络层  
![image](https://user-images.githubusercontent.com/39154923/137895425-44be7955-9425-4853-aefa-390eebe621e1.png)

- UDP：客户端源端口时临时开启的随机端口

### 伪首部
- 源IP 4字节
- 目标IP 4字节
- 零填充 + 协议 + 长度（首部 + 数据部分） 4字节
- TCP与UDP伪首部是一样的
- 仅在计算检验和时起作用
 
### TCP
![image](https://user-images.githubusercontent.com/39154923/138079676-0dfb6af5-a696-465e-9602-5be81e29e9a1.png)

- TCP首部的空间至少是20字节，最大60字节
- 和UDP一样 也有12个字节的伪首部，不会传递到网络层
- 数据偏移占4位，取值范围 0x0101~0x1111，乘4代表首部长度，最小值是5，最大值是15

### TCP要点
- 可靠传输
- 流量控制
- 拥塞控制
- 连接管理

### 数据长度
- UDP首部用16位记录整个UDP数据加首部的长度
- TCP用4位标记了TCP报文首部长度，并没有记录数据长度 
- 传输层的数据长度 = 网络层的数据长度 - 网络层的首部长度 - 传输层的首部长度

### TCP flag含义

- URG：Urgent，紧急字段，为1时，表示当前报文段中有紧急数据，需要优先传输，尽快传送
- ACK：Acknowledgment 确认字段，当ack为1时，首部中的确认号字段才有效
- PSH：Push 交互式网络
- RST：Reset，为1时，表明链接中出现严重差错，必须释放连接，然后重新创建连接
- SYN：Synchronization
  - SYN = 1，ACK = 0时，表明这是一个建立连接的请求
  - SYN = 1，ACK = 1时，表明对方同意建立连接
- FIN：Finish，为1时，表明数据已经发送完毕，要求释放连接

### 序号
- 4个字节
- 传输过程中，每一个字节都会有一个编号
- 建立连接后，序号代表：这一次传给对方的TCP数据部分的第一个字节的编号

### 确认号
- 4个字节
- 建立连接后，确认号代表：期望对方下一次传过来的TCP数据部分的第一个字节的编号

### TCP 可靠传输
- 三次握手
- 四次挥手
- ARQ协议（Automatic Repeat Request） 自动重传协议，超时重传
    + 每个包重试次数过多会发送reset报文，重新创建连接

- 滑动窗口协议：批量发送，批量确认，从确认的报文的下一个开始重新发送
    - SACK(selective acknowledgment)，选择确认，放在TCP首部的选项部分
- 拥塞控制

#### SACK 选择确认
- 放在TCP首部的选项部分
- kind 1字节，值为5代表是sack
- length 1字节，代表sack部分的长度占用多少字节
- left range 左边界 4字节
- right range 右边界 4字节
- 每个选项部分最多携带4组边界值
    - 选项部分最长40字节
    - 4 * 8 + 2 = 34

### TCP 流量控制
- 不进行流量控制接收方已经无法接受了，发送方还在发送，造成浪费
- `点对点`的控制发送方的发送速率
- 通过读取接收方的窗口大小，来控制发送方发送数据的多少
- 发送方的窗口 `<=` 接收方的窗口大小
- 接收方窗口为0，发送方停止发送，如果还有报文要发送，就定时询问接收方的窗口大小

### TCP 拥塞控制
- 防止过多的数据注入到网络中
- 防止链路过载或网络中传输的硬件过载

#### 方法
- 慢开始：cwnd 指数级增长
- 拥塞避免：到达慢开始阈值后，cwdn线性增长
- 快速重传：连续收到同一个报文的3次确认后，立即重新发送下一个报文
- 快速恢复：遇到网络拥塞，然后cwnd的起始值变为慢开始阈值的一半，线性增长

> - MSS: Maximum Segment Size 每个数据段的数据最大大小
> - cwnd congestion window 拥塞窗口
> - rwnd receive window 接收窗口
> - swnd send window 发送窗口
> - swnd = min(cwnd, rwnd)
> - slow start threshold 慢开始阈值，到达阈值后，cwdn 线性增长

### TCP 连接管理
- 连接时三次握手
- 断开连接时四次挥手

#### TCP 建立连接 三次握手

- closed 客户端处于关闭状态
- listen server处于监听状态，等待客户端连接
- syn-rcvd server端收到了客户端发送的建立链接报文后的状态
- syn-sent client端发送了建立连接报文后的状态
- established 连接建立状态
    - client收到了server返回的确认建立连接报文的状态
    - server端收到了client返回的对建立连接报文进行确认的报文后的状态

![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/tcp_handshake.png)

- 前两次建立连接的特点
    - 数据部分长度都为0
    - SYN都设置为1
    - 首部长度为32字节（20字节首部 + 12字节选线部分）
    - 会交换MSS，是否支持SACK，和窗口大小


##### 为什么不采用2次握手
- 两次握手，服务端收到客户端的请求就建立连接，容易造成浪费资源
- 客户端发送的链接建立请求因为网络波动，导致超时送达，此时链接已经释放
- 如果采用两次握手，服务端会对这个延迟的报文进行确认建立连接，但是客户端并无链接意愿，就会造成服务端连接浪费

##### 第3次握手失败了，怎么处理
- 因为网络问题，导致server并没有收到client端的3次握手
- 服务端超时时间内，重新发送SYN + ACK确认包
- 如果发送多次确认包都没有收到回复，则会发送RST包，强制关闭连接

#### TCP 断开连接 四次挥手
- `fin_wait1` 主动关闭链接，并且向对方发送了关闭链接报文
- `fin_wait2` 收到对方发送的ACK确认报文，然后等待对方发送关闭连接报文
- `close_wait` 收到对方发送的关闭链接报文，并向对方回复了ACK确认报文
    - 判断对方自己是否还有数据要发给对方，有就继续发送数据
    - 没有就发送FIN报文表示自己也想关闭连接

- `closing` 双方同时收到了对方发送的FIN报文
- `last_ack` 被动关闭的一方，在发送完FIN报文后，等待另一方确认
- `time_wait` 收到了对方的FIN报文，并回复了ACK，等2MSL时间后就自动进入`closed`状态
- 如果在`fin_wait1`状态据收到了对方的FIN和ACK一起的报文，就可以直接进入到`time_wait`状态
- `closed` 关闭状态

![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/tcp_wave.jpg)

#### TCP 断开连接细节
- 连接双方任意一方都可以主动关闭连接
- 全双工通信，双向关闭连接
- 主动关闭一方发送对另一方的FIN报文确认的ACK后，需要等待2MSL
    - MSL（Maximum Segment Lifetime）TCP报文在internet中最大生存时间

#### 一方发送ACK后，断开连接
- 如果网络波动，造成另一方没有收到ACK，就会重复发送FIN
- 忽略FIN，造成另一方资源浪费
- 接收FIN，造成端口相同的新连接在收到报文后响应关闭连接