许久不查TCP相关的问题,今天下班前被一同事拦下要帮忙,说他碰到了奇怪的问题。
拿下wireshark抓到的包一看,半天才明白他所说的疑惑是指他每次发送一个数据包,通信对端就回了一个ACK包,由此就直接怀疑是否对方关闭连接或者建立新的连接了。
花了半天功夫,总算解释清楚ACK包其实是很正常的数据包(带数据的包也有ACK标志的,wireshark只不过是把不带数据的纯协议ACK包在描述信息里边直接标出来了而已),同事也算是个很老练的Java高手了,对这点基本的小问题有一些疑义,起初是让我有点疑惑的。
不过总算讨论清楚了这个ACK没有任何问题,本以为他遇到的根本不是问题,岂料他又抛出了一个问题:
既然ACK不是造成问题的症结,为什么我要发送三个数据包,只有前一个的ACK收到之后,下一个包才能发的出去?每个数据包的发送和受到ACK的时间间隔大于15ms,而他们的系统需求规定那个间隔必须小于15ms。
这个问题算是有点深入一点了,即使认为15ms的延迟是正常的TCP协议栈行为,那么他的三个包只能顺序发出去就有些诧异了,而且据说是上千个设备都是如此规律,那么这种规律本身就不正常了。
首先的怀疑当然是TCP的buffer满了,导致send发送阻塞,不过TCP的数据内容倒是显示没有那个问题,因为他发送的三个包每个都只有几十个字节。
剩下的情况大概只有一种,就是应用程序手工设置了buffer大小,甚至是设置了SND_BUF为0(其实只要小于他的最小PDU长度),导致他的协议交互变成了“停等协议”了;因为每一次发送的时候,buffer缓冲都不够用,所以send调用必然是被阻塞,直到收到前一个包的ACK数据然后才能继续;不熟悉TCP协议栈的,看到这种现象,就怀疑是那个ACK回复的有问题了。
最后他又提出了一个问题,为什么有时候他一次发送了三个包,抓包的时候只有两个?恰巧这又是一个TCP控制选项的问题,鼎鼎大名的“Nagle算法“在底下运作的结果了。
为了确认猜测不是问题,让他Show了一下代码,确认两种现象对应的是不同的socket,可惜的是后一个socket的创建代码是无法看到了。
这些小选项引起都是非常基本的TCP协议栈原理性知识,为何习惯了Java抽象和自带类库的人会被这种问题产生的表面现象所疑惑?