1 利用滑动窗口实现流量控制
1.1 滑动窗口
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。
利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
1.2 产生死锁
假设我告诉了对方我的接受窗口设置为 0(缓存没有空间了),过了一段时间后,缓存又空出来了,这时发送了一个设置接受窗口为非零值得报文给对方。但是这个报文如果丢失的话,就会产生死锁。因为两边都在等对方发送数据。
1.3 持续计时器
- TCP 为每一个连接设有一个持续计时器。
- 只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。
- 若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带 1 字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
- 若窗口仍然是零,则收到这个报文段的一方就重新设置持续计时器。
- 若窗口不是零,则死锁的僵局就可以打破了。
2 TCP 传输效率
2.1 报文段组装发出
1)发送方
用不同的机制来控制 TCP 报文段的发送时机。
- 第一种机制:TCP 维持一个变量,它等于最大报文段长度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去。
- 第二种机制:由发送方的应用进程指明要求发送报文段,即 TCP 支持的推送(push)操作。
- 第三种机制:发送方的一个计时器期限到了,这时就把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去。
2)接收方
- 推迟确认
- 捎带确认
- 捎带确认就是指,收到数据后不立刻发送确认,而是等到有真的数据要传递时,再设置确认
2.2 Nagle 算法
发送方广泛采用 Nagle 算法。这个算法可用于解决“小包”问题(糊涂窗口综合征)。
Nagle 算法的基本定义是任意时刻,最多只能有一个未被确认的小包。
1)Nagle 算法的规则
Nagle 算法只允许一个未被 ACK 的包存在于网络,大包 MSS 用流水线,小包用停等协议。
- 如果包长度达到 MSS,则允许发送;
- 如果该包含有 FIN,则允许发送;
- 设置了 TCP_NODELAY 选项,则允许发送;
- 未设置 TCP_CORK 选项时,若所有发出去的小数据包(包长度小于 MSS)均被确认,则允许发送;
- 上述条件都未满足,但发生了超时(一般为 200ms),则立即发送。
2)Nagle 算法的流程
TCP 想尽量让每个包都组成 MSS 大小发送出去,不浪费。
- 当缓冲里有一些数据大于 MSS,就把数据先拆成 n 个 MSS 包,然后用 pipeline 方式发出去
- 末尾的小包不立刻发送,而是等待下一个发送的时机,比如时间到了,设置了某些字段,又或者是跟新的数据组装到了一起达到 MSS 才发出去
- 除非之前的小包得到了确认,否则就不把小包发出去
所以这里有点像是只针对于小包的停止等待协议,保证了只有一个未确认的小包在网络中。一旦包大于 MSS,都是使用 pipelining 的方式发送。
3)糊涂窗口(零窗口)综合征
在流量控制中,会遇到糊涂窗口综合征这个问题。就是指当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小; 极端情况下,有效载荷可能只有 1 个字节;传输开销有 40 字节(20 字节的 IP 头 + 20 字节的 TCP 头) 这种现象。
比如说典型的,telnet 中我每次只传输 1 个字符,或者是对方的缓存满了,接受窗口只有 1 字节,我每次只能发送 1。这都导致了糊涂窗口综合征。