TCP的那些怪事兒

weixin_33832340發表於2016-07-27

客戶端Write成功後再Read超時收到reset,伺服器端顯示連連結都沒有建立?

當客戶端第一次建立連結成功後,呼叫write向伺服器傳送請求,返回成功後呼叫read/recv等待回覆卻超時。

如果想搞清楚這個問題,需要掌握TCP三次握手,write系統呼叫到底是如何進行的。本文嘗試一一闡述來解釋這一怪現象。

TCP連線的建立-三次握手

TCP連線的建立大部分是三次握手的過程,傳統的圖如下:

1976071-be0efd63b5a7b825.jpg
三次握手.jpg

客戶端應用程式呼叫connect時會發起握手過程,看一下連線建立的點在哪裡,如圖,當Client收到ACK後connect返回,連線在Client端程式來看已經完成。但Server需要等待下一個ACK才算連線建立。

1976071-4a4a3c53ee909146.png
連線建立.png

那麼問題來了,如果最後一個ACK Server並沒有收到,會有什麼情況發生呢? 我們暫且懸疑,稍後作答。

Write系統呼叫成功代表報文送達了嗎

不論是阻塞的或者非阻塞的socket,當向一個fd write的時候,實際上只是將使用者態的一段記憶體拷貝到了核心態。換句話說,write成功返回並不代表對端已經收到,甚至都不說明報文已經送到了網路上。

1976071-fc313b1ad3a74cf2.png
tcp-write.png

看一下這幅圖,呼叫write返回到報文送到網路上要經過多麼複雜的流程吧:首先放置到tcp send buffer,經tcp協議棧,ip層,擁塞控制,DMA讀寫,然後一箇中斷才經網路卡驅動傳送出去。
所以,使用者程式write返回後,幾乎什麼都代表不了,只是成功的傳送到了核心態。聽起來很悲劇。那麼一個引申的問題就是,客戶使用者程式如何得知對端已經接收了上次傳送的報文。答案就是從和server的通訊協議下手,即等待server回覆一個類似確認的報文,很多rpc實現為一請求一應答其實是有根據的。

怪問題的答案

明白了上面兩個問題之後,那麼本文開始處的問題就不言而喻了。首先對於client來講,使用者程式認為connect建立成功,write雖然成功,但不能說明什麼,而server由於最後一個ack沒有收到連線其實沒有建立成功。不知道這個解釋各位看官是否滿意_

相關文章