許久不查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的創建代碼是無法看到了。
這些小選項引起都是非?;镜腡CP協議棧原理性知識,為何習慣了Java抽象和自帶類庫的人會被這種問題產生的表面現象所疑惑?