TCP网络常见调优参数之一:Nagle算法

客户端和服务端网络交互伪代码:

案例分析:

         在服务端通过tcpdump抓包发现写数据过程中,少量网络包在接收到客户端发送过来的协议头后,并没有及时回复对方tcp ACK报文,而是等待几十个毫秒后才回复ACK(其他大部分情况都是及时回复ACK)。默认情况下TCP连接上最多只能有一个未被确认(未收到Ack确认)的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组,因此服务端接收到协议头后,为及时回复ACK时,客户端也因此产生了一个未被确认的ACK包,进而不再发送协议体。产生上述现象原因是TCP延迟ACK机制和默认开启的Nagle 算法机制相互牵制导致。

1.延迟ACK
         tcp协议规定在接受到数据段时需要向对方发送一个确认,但如果只是单纯的发送一个确认,代价会比较高(20字节的ip首部,20字节的tcp首部),最好能附带响应数据一起发送给对方。所以tcp在何时发送ACK给对方有以下规定:
1) 当有数据要发送时,ACK会伴随数据立即发送给对方。

2) 如果没有响应数据,ACK的发送将会有一个延迟,以等待看是否有响应数据可以一起发送,这称是"Delayed Ack"。但这个延迟最多不会超过40ms(可以配置内核参数),如果在40ms内有数据要发送,那么ACK会随数据一起立即发送给对方。

3) 如果在等待发送ACK期间,对方的第二个数据段又到达了,这时要立即发送ACK.但是如果对方的三个数据段相继到达,那么第二个数据段到达时ACK立即发送,但第三个数据段到达时是否立即发送,则取决于上面两条。
2.Nagle 算法
       Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块(通常长度为1460字节),“未被确认”是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。在该分组的确认到达之前不能发送其他的小分组。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。

解决过程:

客户端发送一个协议头后,紧接着数据体,且数据体长度小于MSS长度(少于1460字节),此时Nagle算法发挥作用,即不允许有一个以上未被确认的“小段”数据包,因此客户端会等待服务端接收到协议头ACK报文后发送数据体。但是服务端先接收到协议头后,由于TCP延迟ACK机制默认也是开启的,所以服务端接收完协议头并没有及时回复ACK,而是在超时范围内等待,如果服务端收到下一个网络包或者有应答数据返回给客户端时才立即回复ACK,本方案中网络交互模型恰好是服务端Recv完协议头后,又继续Recv数据体,此时因为没有及时ACK,导致与客户端互相依赖性的等待,最终40ms超时后才可以继续交互。

在服务端可以关闭延迟ACK机制,即接收tcp包后立即响应ACK。代码如下。

 int flag = 1;

 int result = setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK,  (char *) &flag, sizeof(int));   
这种解决方法是直接关闭该机制,但同时也会导致大量单独的ACK报文,因为默认情况下,ACK报文可以不用单独发送,通常在服务端响应回复消息中捎带ACK即可。
另外一种方案是不更改TCP这些默认设置,而是在发送端将协议体和数据体在发送前拼接成一个报文调用send发送,也可避免上述问题。    

经验总结:

客户端网络交互时尽量避免多个小的TCP报文连续发送,可以将小报文拼接成一个整体一次性发送。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值