1. TCP 首部解析
- 源端口, 目的端口: 使用
TCP
协议传输数据时, 从本机发送出去时通过的端口和目标机器用于接收的端口 - 序号(Sequence Number): 用于标记相应的
TCP
报文的第一个字节的一个编号(拆分TCP
报文发送时,Seq
表示该报文中的数据相对于要发送的所有数据的偏移量)
- 确认号(Acknowledgement Number): 若
Ack=x
, 则表示期望对方下一次发过来的数据的Seq=x
- 数据偏移(Data Offset): \(DataOffset = size(TCPHeader) \div 4\)(标识了
TCP
首部的大小). 由数据偏移占4位可知,TCP
首部长度最多 60 个字节 - 保留位(暂时没有意义)
- 控制标志(Control Flags):
- URG: 当该位值为 1 时, 对应的紧急指针有效
- ACK: 当该位值为 1 时, 表明确认号有效
- PSH: 当该位值为 1 时, 收到相应报文的主机会尽快将数据交付给相应的程序, 而不会等到缓存满了再向上交付
- RST: 当该位值为 1 时, 表示网络情况不好, 需要释放连接后, 重新建立连接
- SYN: 当该位值为 1 时, 表示期望与对方建立连接
- FIN: 当该位值为 1 时, 表示期望与对方断开连接
- 窗口大小(Window Size): 用于表示接收/发送窗口的大小
- 检验和(Check Sum): 通过对 伪首部+首部+数据部分 的计算所得出来的一个检验参照
- 紧急指针(Urgent Pointer): 设其值为
k
, 则表示该TCP
数据的前k
个字节是紧急数据 - 选项(Options)
- 常见选项之一: 启用
SARQ
协议 - 常见选项之二: 传递
MSS
- 常见选项之一: 启用
2. TCP 的三次握手
- 客户端向服务器发送连接建立请求: SYN = 1, Seq = x
- 服务器响应客户端的请求, 表示准备好发送数据了: SYN = 1, ACK = 1, Ack = x + 1, Seq = y
- 客户端回复服务端的相应, 请求服务端发送数据: ACK = 1, Seq = x + 1, Ack = y + 1
x 和 y 都是是一个随机生成的数字 ISN(Initial Sequence Number)
2.1 为什么需要三次握手, 而不是两次
2.1.1 避免服务器的资源被无端占用
- 若 client 的第一次建立连接的请求被延迟到与 server 的连接断开后才到 server
- 会导致 server 单方面地认为自己与 client 建立连接了
- 这样的情况会使得服务器的资源无端占用
2.1.2 确保 client 与 server 双方都能够发送和接收数据
第二次握手, server 向 client 发送的响应头中包含的关键数据
- Ack = clientISN + 1: 表示 server 准备好接收来自 client 的请求了
第三次握手, client 向 server 发送的响应头中包含的关键数据
- Ack = serverISN + 1: 表示 client 可以接收 server 发来的数据了
如果没有第三次握手, 则只能由 client 向 server 发送请求, 而 client 无法接收来自 server 的数据
3. TCP 的四次挥手
- client 向 server 发起释放连接的请求(不会主动向 server 发送数据): FIN = 1
- server 向 client 返回允许释放连接的响应: ACK = 1
- 若 server 还有没有发送完的数据, 则不会发送 FIN = 1, 并继续发送数据
- 若 server 没有需要发送的数据了, 则2, 3步合并, 发送 ACK = 1, FIN = 1
- server 数据已经发送完毕, 向 client 发送释放连接的消息: FIN = 1
- client 响应 server 的数据: ACK = 1
- server 收到来自 client 的确认, 立即断开连接
- client 等待 2MSL 后断开连接(若在这期间收到了来自 server 的信息, 则重新定时)
3.1 为什么 client 必须要等待 2MSL 后才断开连接
3.1.1 保证本次连接所发送的数据不会干扰到下一次的连接
MSL(maximum segment lifetime), 指的是 TCP 报文在网络上的最大生存时间, 一般是 2 分钟, 也可以自己设定. MSL = 2 可以确保当前连接中所传输的内容全部传输完毕
可以想象, 若 client 发送 ACK = 1 后立即断开连接, 而 server 并没有收到该确认消息, 则 server 会继续继续发送 FIN = 1
- server 可能会重发多次 FIN = 1 的信息, 浪费资源
- 一个应用程序继承了 client 的同端口, 可能会被 server 发来的信息干扰到
若 client 等待 2MSL 后才断开连接:
- client 发送的 ACK = 1 的信息 server 并没有收到, 则 server 会继续向 client 重传 FIN = 1 的消息, 而 client 收到 FIN = 1 后, 会重新发出 ACK = 1 的响应, 并重置定时器.
- 一般来说, FIN = 1 的消息会在 2MSL 内到达 client, 若 FIN = 1 多次重传也没有收到 ACK = 1 的响应, 则其会判断网络拥塞, 自己断开连接