TCP默认做了一些带宽利用率、性能方面的优化,实际应用中这些优化可能会导致莫名其妙的问题
Nagle算法
Nagle算法用于减少网络中小的packet的数量,从而降低网络拥塞程度。
举个例子,telnet连接后,用户在控制台每次按键都会发送一个packet,这个packet通常包含41个字节,然而只有一个字节是payload,其余40个字节都是header,如果每次按键都发送一个packet,那会造成巨大的开销。
为减少这种开销,Nagle算法指出,当TCP发送一个小于MSS的segment时,它必须等到接收到对方的ACK后,才能继续继续发送另一个小的segment。那么在等待过程中(一个RTT时间),TCP就能尽量多地将要发送的数据收集在一起,从而减少要发送的segment数量。
默认情况下,TCP开启Nagle算法,Nagle会引入负面影响,它会增加TCP发送数据的延迟。在一些要求低延迟的应用程序中(比如即时通讯),需要禁用Nagle算法,可以通过设置TCP_NODELAY
标识禁用Nagle算法
|
|
Delayed ACK
TCP的Delay ACK与Nagle算法有异曲同工之妙,当TCP接收到数据时,并不会立即发送ACK给对方,相反,它会做如下选择:
- 在40MS内如果正好有packet发送给client,那么这个ack就跟着发过去(顺风车,不需要增加packet大小)。
- 40MS内如果凑够了2个ACK也直接发送包返回给client
- 如果等待超过40MS,那么也会自己发一个ack包
通过设置TCP_QUICKACK
标识调整为快速确认模式
TCP采用两种方式发送ACK: 快速确认和延迟确认
在快速确认模式下,本端接收到数据包后,会立即发送ACK给对端
在延迟确认模式下,本端接收到数据后,不会立即发送ACK给对端,而是等待一段时间,如果在此期间
本端有数据包要发送给对端,就在发送数据包时捎带上此ACK,如此一来节省了一个报文
本端没有数据包要发送给对端。延迟确认定时器会超时,然后发送纯ACK给对端
TCP CORK
Linux提供了TCP_CORK选项,如果某个TCP socket上开启了这个选项,那么就相当于在这个socket出口堵上了塞子,往这个socket写入的数据都会聚集起来。虽然堵上了塞子,但是segment总得发送,不然数据会塞满整个TCP发送缓冲区,以下几种情况都会导致塞子打开:
- 程序取消设置TCP_CORK选项
- socket聚集的数据大于一个MSS的大小
- 自堵上塞子写入第一个字节开始,已经超过200MS
- socket被关闭了
满足以上任何一个条件,TCP就会将数据发送出去。对于Server来说,发送HTTP响应既要发送尽量少的segment,同时又要保证低延迟,那么需要在写完数据后显式取消设置TCP_CORK选项,让数据立即发送出去。
比如nginx中在sendfile模式下,可以设置打开TCP_CORK选项tcp_nopush on
(TCP_NOPUSH
与TCP_CORK
两个选项实现功能类似,只不过NOPUSH是BSD下的实现,而CORK是Linux下的实现)
FAQ
1.如果client启用Nagle,并且server端启用了delay ack会有什么后果
假如client要发送一个长度为1600字节的请求,握手的MSS为1460,这个请求会拆为2个包,一个1460,一个140。第一个包发送至server后,server由于delay ack会延迟ack,同时由于server没有接受全请求,也无法回复http请求,而client的第二个包由于是小包,受nagle算法影响,会一直等待上一个包的ack,直至server的dalay ack超时(40ms),此时client收到第一个包的ack,接下来发送第二个包