問題產生:
在進行客戶端向服務端發送數據時,每次發送一定數量數據后發送端就等不到send函數的返回,導致程序一直卡死在send函數。
通過抓包發現:發送端發送過快而接收端處理速度過慢,導致快速發送一定量數據后wireshark顯示發送端發送數據有window full提醒,幾次之后接收端會發送zero window消息,發送緩沖區數據無法發出導致堆積滿發送緩沖區,從而導致send無法將數據拷貝進發送緩沖區,進而形成send函數無法返回,程序阻塞無法運行。
分析:
recv端表現:在剛開始發送數據時,接收端處於慢啟動狀態,滑動窗口值越來越大,但是由於接收端不處理接收緩沖區內的數據,其滑動窗口越來越小(因為接收端回應發送端中的win大小表示接受端還能夠接受多少數據,發送端下次發送的數據大小不能超過回應中win的大小),最后發送端回應給接受端的ACK中顯示的win大小為0,表示接收端不能夠再接受數據。
send端表現:發送端一直不能返回,如果接收端一直回應win為0的情況下,發送端的send就會一直不能返回,這種僵局一直持續到接收端的緩沖區數據被處理完成空出足夠接收一定量數據的空間。
原因分析:首先需要明白幾個事實,阻塞式I/O會一直等待,直達這個操作完成;發送端接受到接收端的回應后才能將發送緩沖區中的數據進行清空。
那么接收端的接收緩沖區滿,導致滑動窗口為0,發送端不能發送數據。但是send操作為何不能返回呢?send操作只是將應用緩沖區的數據拷貝到發送緩沖區,但是發送緩沖區的數據並沒有完全得到接收端的ACK回應,所以暫時不能將發送緩沖區中的數據丟棄,導致發送緩沖區的被填滿,這樣應用層中的數據也就不能拷貝到內核發送緩沖區內,也就會一直阻塞在這里,直到可以繼續將應用層的數據拷貝到發送緩沖區中,何時觸發這個操作呢?等到發送端回應win大於0時才有這樣的操作。
遺留問題:
接收端一直在接收數據,將緩沖區數據處理寫入本地文件,但是問題產生后程序將會一直阻塞基本未見好轉情況。同時抓包數據顯示recv端一直有zero window消息,send端一直發送心跳包檢測。為什么程序一直卡死,並且接收端緩沖區不見減小?
-->>解答:
接收端使用ET模式,而且每次接收數據buff設置小於發送的buff,導致每次收到ET消息后只會處理部分緩沖區內數據,導致緩沖區數據持續增長直至窗口縮小為0。
至此,發送端不能再發送數據,接收端將不會收到ET信號。從而緩沖區數據將不會減少,窗口一直保持為0的狀態。程序死鎖,一直hang住。