TCP
TCP 是面向连接的基于字节流的可靠传输
面向连接
TCP 通过收发双方的主机地址和端口确定连接
连接双方交换初始序列号, 已达成有序传输
建立连接
- 客户端发送 SYN
- 服务端发送 ACK + SYN
- 客服端发送 ACK
断开连接
- 发起方发送 FIN
- 接收方回复 ACK
- 接收方发送 FIN
- 发起方发送 ACK
发起方在发送 ACK 之后, 会等待2MSL(Maximum Segment Lifetime). 在此期间,如果收到来自服务端的 FIN, 会重复此步骤, 并重置 2MSL 的等待时间.
TIMW_WAIT
发起方在发送最后一个 ACK 时, 为了等待旧连接的数据包不会干扰新的连接, 通常需要等待2MSL 的时间.
其中一个 MSL 是等待发送的报文过期, 另一个 MSL 是等待发送报文对应的 ACK 过期.
连接状态
| 状态 | 说明 |
|---|---|
| CLOSED | 初始状态,表示连接未建立。 |
| LISTEN | 服务器端等待连接请求,处于监听状态。 |
| SYN_SENT | 主动打开连接的一方已发送SYN包,等待对方确认。 |
| SYN_RCVD(或SYN_RECV) | 被动打开一方收到SYN后发送SYN+ACK,等待对方确认。 |
| ESTABLISHED | 连接已建立,双方可以开始数据传输。 |
| FIN_WAIT_1 | 主动关闭方发送了第一个FIN包,等待对方确认。 |
| FIN_WAIT_2 | 收到对方的ACK,等待对方发FIN。 |
| CLOSE_WAIT | 被动关闭方收到FIN,等待本地应用关闭连接。 |
| CLOSING | 双方几乎同时关闭连接,等待确认。 |
| LAST_ACK | 被动关闭方发送最终ACK,等待确认。 |
| TIME_WAIT | 主动关闭方等待确保对方收到ACK,防止旧包干扰。 |
流式传输
- tcp 将数据按照文本流进行传输
-
数据拆分成多个
segment, 每个`segment -
data
- segment
- header
- sequence number
- payload
- header
- segment

滑动窗口
为了提升收发效率, 按照某种方式, 收发双方各有一个窗口, 用以同时传输多个连续的片段.
当序号最小的片段收到ACK时, 发送方的窗口又移, 发送新的片段直到结束.
接收方当窗口内序号最小的片段发送ACK后, 同样会对窗口右移.
当接收方收到小于窗口序号的数据包时,会丢弃该数据包,并回复ACK, 以避免ACK在网络中丢弃而导致的发送方持续重传
累计 ACK
通过合并多个ACK, 减少 ACK的回复数量.
如在乱序的情况下,在 8、9、10 的窗口下,已经接收到 9、10, 而最后接收到 8, 可以发送 ACK 10 表示接收到所有序号小于 10 的数据包, 将三个 ACK 合并为一个.
思考
累计 ACK 的实现是对于接收到的数据包不是第一时间回复 ACK, 但是不能长期等待序号较大的数据包来合并 ACK, 否则会导致发送方重传
可能会造成网络拥塞导致窗口变小等等副作用
可靠性
TCP 是基于 IP 协议的, 数据包可能会丢失、乱序.
因此 TCP 协议要求接收方在收到符合次序的片段后, 向发送方回传 ACK 回复, 以表示收到该片段.
发送方在一定时间内没有收到ACK后就会重新发送该片段. 直到收到ACK或超时断开连接.
重传
重新发送超时时间(RTO)
RTT: 发送方发出数据包后到接收到ACK的一次往返时延
RTO 是根据发出数据包后设置的定时器, 触发该片段的重传.
定时器触发前接收到该片段的 ACK 则取消.
RTO 过小会导致网络资源被浪费, 过大则会造成资源闲置或丢包后的整体网络.
实际中可能会取决于操作系统对每次 RTT 的平均值和标准差进行计算得到 RTO
快速重新发送(fast-retransmission)
快速重新发送机制利用重复的ACK来提示空洞的存在。
当接收方接收到乱序片段时,会发送对应空洞片段的 ACK.
当发送方收到重复的ACK次数达到阈值, 就认为该片段丢失, 需要重传该片段
快速重新发送机制提高了检测丢失片段的效率,往往可以在超时之前探测到丢失片段,并重复发送丢失的片段。
思考
如果发送方收到了ACK 后导致窗口右移,那重复收到的 ACK 在窗口的左侧, 那么怎么办?
网络拥塞控制
- tcp 建立连接时,会根据接收方系统内核参数, 来决定窗口的大小
- 在通讯过程中, 根据实时的网络情况,收发双方会动态调整窗口的大小以应对丢包等情况