今天下午大雄说在测试过程中发现个奇怪的现象,A,B程序在同一台机器上,A做为server,B做为client。
A给B不停地发数据,数据量比较大,可能100M的数量级。发了一段时间后,发现A的程序日志是write成功了,
但B并没有收到或者很慢才收到。第一反应是捉包,用tcpdump嘛!大雄早就捉好包了,只是看起来很奇怪,
现象是前面30来个包很正常,忽然有个包B回ACK慢了一点点,A接下来等了200MS才发下个包了。然后变态
的事情发生了,接下去A所有的包都是发一个小包,再等200MS再发一个大包。
19901是A的端口,47347是B的端口,第一个红色框就是第一次出现ACK回得慢的地方。后面发生一次窗口为0,
这个下面的包再一起说,由于这里说的原因,这个win是不对的,因为包里没有SYN包,所以不知道放大倍数
(根据猜应该是128倍)所以再捉了一次包,这次现象就更清楚了些:
注:这次A是19901,B是9705
这里跟上次的现象一样,只是出现了杯具的【TCP Previous segment lost】根据之前的经验,这个应该是tcpdump
捉不到数据,丢了比较重要一段,但没关系,下面的情况告诉了我们一些信息,当发包出现意外后,还是A机就塞了
一个15K的包过来,一把将窗口塞满,然后接下去就是重复,A发一个小包,B的ACK回得很快,A等了200MS再发一
个15K的大包塞满窗口,B的ACK照样回得很快。
这里还有个细节要注意,回到SYN包去看,发现MSS竟然有16K大
MSS = MTU – (IP头 +TCP头) 由于是同一台机,所以走的是回环,ifconfig看一个lo的MTU是16436所以MSS就为16396(16436-40)
后来春哥研究了一下,关闭了Nagle算法,问题不再出现。
setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (
char
*)&value,
sizeof
(
int
));
好了,主题来了,什么是Nagle算法?《TCP/IP 详解》的第19章有提起这个,不过由于这本书写得好早,所以只是比较
简单的描述,网上一般将Nagle和糊涂窗口综合症(Silly Window Syndrome)一起讨论,《TCP/IP 详解》的第22章
也有提起,不过这里,这里说得更为详细一下和与时俱进一些。详细的原理,大家参与上面两个链接就行了,我这里简单
比喻一下。
网络就好比一条铁路,带宽就像这条铁路全部排满能停多少车厢假设1000个。MTU就好比一辆火车最多能拉多少车
箱(算上火车头),火车头就好像是IP头+TCP头,车箱就是MSS,这里假设MTU为100节,那MSS就为99,因为要减去火车头。
同时我们规定,从A出发到B的车,一次发多少车箱,要取决于两个条件:
那么带宽使用率最高的情况是,开满10趟车,一车带99个车箱。而利用率最低的情况是一个车头带一个车箱,500趟。
第一种只要10个头就能带990个车箱过去,而第二种要500个头只能带500个。后面这种叫小包,这种情况就叫糊涂窗口综合症。
这种情况有可能是两方面引起的。
而Nagle算法就是为了减少小包,解决问题1的方法之一,他的思路其实就是停,等。等你装多点车箱我再走。
他要满足以下主要条件:
带慢99节车箱跑可以,但你在路上最多只有一辆车可以这样,只有B站通知你这个车到了,你才能再发一辆。
这样看,我们似乎是要受到Nagle算法的限制的。因为MSS好大有16K,而我们的包一般最大只有3K左右,也就是说正常
情况下,我们只会是A发一个包,B回一个ACK,A再发一个包。是的,前面一直是这样的。但后面为何为有200MS的出现呢?
按照Nagle算法,你回了ACK,我就可以再发一个包了,也就是不用等200MS的超时。我们的例子可以看出B回ACK还是
很快的,除了出事前的最后一次慢了一点点。所以关闭Nagle算法会不会是这个问题的冶本方案呢?这个200MS是不是
真的Nagle算法引起的呢?从目前的包来看,并不符合Nagle算法的描述,也不符合超时重发,所以这个很奇怪!
各位大侠有什么建议麻烦给我说一下!
个人猜测可能有以下原因:
1.同一台机,Tcpdump捉的数据不一定是准确的。
2.可能跟操作系统,或者进程的缓冲区有关,因为本地发MSS太大,速度太快。
3.有空可以看看linux的源码,tcp_nagle_check函数