許久不查T(mén)CP相關(guān)的問(wèn)題,今天下班前被一同事攔下要幫忙,說(shuō)他碰到了奇怪的問(wèn)題。
拿下wireshark抓到的包一看,半天才明白他所說(shuō)的疑惑是指他每次發(fā)送一個(gè)數(shù)據(jù)包,通信對(duì)端就回了一個(gè)ACK包,由此就直接懷疑是否對(duì)方關(guān)閉連接或者建立新的連接了。
花了半天功夫,總算解釋清楚ACK包其實(shí)是很正常的數(shù)據(jù)包(帶數(shù)據(jù)的包也有ACK標(biāo)志的,wireshark只不過(guò)是把不帶數(shù)據(jù)的純協(xié)議ACK包在描述信息里邊直接標(biāo)出來(lái)了而已),同事也算是個(gè)很老練的Java高手了,對(duì)這點(diǎn)基本的小問(wèn)題有一些疑義,起初是讓我有點(diǎn)疑惑的。
不過(guò)總算討論清楚了這個(gè)ACK沒(méi)有任何問(wèn)題,本以為他遇到的根本不是問(wèn)題,豈料他又拋出了一個(gè)問(wèn)題:
既然ACK不是造成問(wèn)題的癥結(jié),為什么我要發(fā)送三個(gè)數(shù)據(jù)包,只有前一個(gè)的ACK收到之后,下一個(gè)包才能發(fā)的出去?每個(gè)數(shù)據(jù)包的發(fā)送和受到ACK的時(shí)間間隔大于15ms,而他們的系統(tǒng)需求規(guī)定那個(gè)間隔必須小于15ms。
這個(gè)問(wèn)題算是有點(diǎn)深入一點(diǎn)了,即使認(rèn)為15ms的延遲是正常的TCP協(xié)議棧行為,那么他的三個(gè)包只能順序發(fā)出去就有些詫異了,而且據(jù)說(shuō)是上千個(gè)設(shè)備都是如此規(guī)律,那么這種規(guī)律本身就不正常了。
首先的懷疑當(dāng)然是TCP的buffer滿了,導(dǎo)致send發(fā)送阻塞,不過(guò)TCP的數(shù)據(jù)內(nèi)容倒是顯示沒(méi)有那個(gè)問(wèn)題,因?yàn)樗l(fā)送的三個(gè)包每個(gè)都只有幾十個(gè)字節(jié)。
剩下的情況大概只有一種,就是應(yīng)用程序手工設(shè)置了buffer大小,甚至是設(shè)置了SND_BUF為0(其實(shí)只要小于他的最小PDU長(zhǎng)度),導(dǎo)致他的協(xié)議交互變成了“停等協(xié)議”了;因?yàn)槊恳淮伟l(fā)送的時(shí)候,buffer緩沖都不夠用,所以send調(diào)用必然是被阻塞,直到收到前一個(gè)包的ACK數(shù)據(jù)然后才能繼續(xù);不熟悉TCP協(xié)議棧的,看到這種現(xiàn)象,就懷疑是那個(gè)ACK回復(fù)的有問(wèn)題了。
最后他又提出了一個(gè)問(wèn)題,為什么有時(shí)候他一次發(fā)送了三個(gè)包,抓包的時(shí)候只有兩個(gè)?恰巧這又是一個(gè)TCP控制選項(xiàng)的問(wèn)題,鼎鼎大名的“Nagle算法“在底下運(yùn)作的結(jié)果了。
為了確認(rèn)猜測(cè)不是問(wèn)題,讓他Show了一下代碼,確認(rèn)兩種現(xiàn)象對(duì)應(yīng)的是不同的socket,可惜的是后一個(gè)socket的創(chuàng)建代碼是無(wú)法看到了。
這些小選項(xiàng)引起都是非常基本的TCP協(xié)議棧原理性知識(shí),為何習(xí)慣了Java抽象和自帶類庫(kù)的人會(huì)被這種問(wèn)題產(chǎn)生的表面現(xiàn)象所疑惑?