2. tcp是一种高效的协议吗
TCP本身并不是一个完美的协议, 尽管它保证了可靠性与顺序, 但是还是存在一些效率问题, 比如你在信号很差的地方, 那让人抓狂的网速,这篇文章分析一下有没有什么办法能提升TCP的效率.
多包并发的能力
在我们之前的描述中我们把TCP的通讯过程描述成一来一回的回合制模式, ACK的包序应答的确能解决可靠性+乱序的问题, 但如果你真搞一来一回就太蠢了, 你为什么骂人? 分析一下你就懂了.
假设你在北京往上海的服务器发5个包, 直线距离1000km, 就算光速传包延迟至少也会有5ms, 一来一回(我们把这个叫RTT)就是10ms, 实测40ms, 三次握手消耗1.5RTT, 5个包5RTT, 这样一趟就是6.5RTT, 两百毫秒下去了, 你觉得这合理吗?
假设你用的是10M网, 一秒之内就能发10*10^6/8字节的数据, 这么大的吞吐能力, 你完全能做到三次握手之后, 一轮就把5个包全发了, 服务器直接回答ACK=5代表5个包全收到,这样只消耗2.5RTT, 100毫秒, 强不强?
以上行为我们叫它"压榨带宽", 我们通过上面的例子证明, 确实存在一些必要, 一些可能, 去让你的TCP通讯效率更高, 我们在例子中一共引入了三个问题
TCP中的窗口大小就是做这个事情的, 我们现在已经知道了能一次发很多包了, 但是发多少呢? 如果发太多. 可能发的速度比接收方收的速度还快. 双方会约定一个RWND
代表你一次最多能发多少.
Nagle算法提升带宽利用效率
我们在上一节提到了TCP的拆包是因为MTU与MSS搞鬼, 但是没提到其实将几个小包合并成一个大包也是有可能的. 合包的做法起源于一个叫Nagle的算法, 这个算法的目的是想要提高宽带的利用效率.
我们先举例说明为什么你的宽带可能利用效率不高, 我们经常使用一个叫做Telnet的工具, telnet协议发出的包都是内容1字节, 但是有40字节的头(20IP+20TCP), 这样的一个包发出去了 带宽使用效率只有(1/41=2.44%), 导致了带宽使用效率不高的情况
与其这样不如将一些小包合并成一个大包一起发, 这样利用效率就高了. 当应用层通过TCP协议发消息的时候, 实际上待发送的消息会先写入TCP的缓冲区. 在Nagel算法工作的情况下, 消息不会立刻发出而是等待到MSS长度或上一条消息ACK了才会发
Nagle潜在的问题: Delayed ACK
Nagle是tcp中的一个把戏, delayed ack是tcp引入的第二个把戏, 具体玩法是这样的: 收到一个包以后不会立刻做ack响应, 而是等待以下某个条件触发以后再做响应:
又来包了, 然后几个包的序号Merge一下响应最后一个ACK
我也要发消息, 那么我直接把ACK埋在我自己这个包里一起带过去
超时了, 时间到了, 必须该响应了
这种把戏单独看好像没什么问题, 但是跟nagle一起就可能有问题, 试想以下场景: 客户端有一个比较大的消息要发, 那么就拆两段发咯, 先发第一段, 但是服务端收到第一条消息以后, delayed ack发挥作用, 不做响应, 此时客户端的nagle发挥作用, 一定要等到响应或者攒到MSS才肯发.
双方就这么僵持着, 直到delayed ack超时, 服务端才肯响应, 通信才能继续, 就这么搞白白浪费了不少时间. 关于这种问题, 我们后面会在KCP协议中做出优化, 因为KCP根本不管这些, 为了速度就会立刻做出响应
Introduction
我们在上篇介绍过我们会使用RWND
字段控制批量发包, 但是发的越多, 就代表丢失其中一两个的可能性也更大. 在很久以前就有人想办法说如果丢包了, 那就是网络拥堵了, 你再发这么多, 再丢一批, 然后你疯狂重试, 这样只会让网络环境越来越堵, 我们控制一下吧, 丢包了下一批就少发一点, 减少发送数量就会降低拥堵.
TCP拥塞控制
拥塞控制算法启动后, 工作的过程也很有意思, 我们同时维护两个字段:
慢启动阈值,ssthres, 这个字段用于控制发包数量的增减
拥堵窗口,cwnd,这个字段跟接收窗口一起控制发包数量的最大值
min(cwnd,rwnd)
发包数量的增减控制则是:
如果cwnd小于ssthres, 那么每轮过去cwnd翻倍
如果cwnd大于ssthres, 那么每轮过去cwnd加一
如果出现丢包, 那么cwnd减半, 继续上面两步
举例说明, 默认的cwnd是10, 也就是我们当前最多可以发10个包:
第一轮过去了, 变成20, 40
开始超过ssthres, 一轮加一个, 41,42
出现丢包, rwnd减半一轮发21个, 42个
拥塞控制带来的效率损失
按照这种方式, 我们需要经过好多轮才能达到最大吞吐, 假设我们要发的包本身就不多, 可能你刚到最大吞吐, 会话就结束了, 你全程都没用上最大吞吐能力. 浪费了不少RTT.
回头想想这种拥塞控制到底有没有必要, 他搞这一套无非是因为他认为丢包就是网络拥堵, 他说这个话, 是因为在他那个年代大家都用有线网, 不太可能出现莫名其妙丢包的情况
但是现在都用无线网了, 丢包原因五花八门, 丢包不见得就是网络拥堵, 因此降低拥堵也不见得就能解决你的问题. 有一个非常有趣的思路, 就是我不相信是网络拥堵搞的我丢包, 我搞更激进的策略发包, 有时候他们这种思路效果居然更好
重传机制带来的效率损失
假设我们一次发了5个包, 但是第一个丢了怎么办呢? ACK的中的数字是指"在此之前的所有包都已经收到了", 可是我只收到2345, 1没收到, 我没法给你应答一个5, 我只能应答0, 这下2345又要重发了
这种问题可以由快重传机制解决, 0没收到但是收到了2345, 连续三下ACK0, 提示你0没收到, 对面就会只发一个0过来了
Last updated
Was this helpful?