前段時間研究分布式時寫了一個可擴展的服務(wù)器組程序,服務(wù)器組之間通信時老是達不到想要的性能。后來抓包排查,原來是TCP滑動窗口引起的問題,本來是很基礎(chǔ)的東西,奈何當(dāng)初沒有太在意,導(dǎo)致錯誤的產(chǎn)生,現(xiàn)在詳細(xì)寫出來,忘不太清楚者警惕!
滑動窗口的基本情況我有必要廢話一下。TCP通信為了保證可靠性,每次發(fā)送的數(shù)據(jù)都需要得到對方的ACK才確認(rèn)對方收到了(僅保證對方TCP接收緩沖收到數(shù)據(jù)了,但不保證對方應(yīng)用程序取到數(shù)據(jù)了),這時如果每次發(fā)送一次就要停下來等著對方的ACK消息,顯然是一種極大的資源浪費和低下的效率,這時就有了滑動窗口的出現(xiàn)。
發(fā)送方的滑動窗口維持著當(dāng)前發(fā)送的幀序號,已發(fā)出去幀的計時器,接收方當(dāng)前的窗口大小(由接收方ACK通知,大體等于接收緩沖大小-未處理的消息包),接收方滑動窗口保存的有已接收的幀信息、期待的下一幀的幀號等,至于滑動窗口的具體工作原理這里就不說了。
一個socket有兩個滑動窗口(一個sendbuf、一個recvbuf),兩個窗口的大小是通過setsockopt函數(shù)設(shè)置的,現(xiàn)在問題就出在這里,通過抓包顯示,設(shè)置的窗口大小沒有生效,最后排查發(fā)現(xiàn)setsockopt函數(shù)是后來加上的,寫到了listen函數(shù)的后面,這樣每次accept出的socket并沒有繼承得到主socket設(shè)置的窗口大小,無語啊……
解決辦法:setsockopt函數(shù)提前到listen函數(shù)之前,這樣在服務(wù)器程序啟動監(jiān)聽前recvbuf就已經(jīng)有了,accept后的鏈接得到的就是recvbuf了,啟動程序運行,抓包顯示窗口已經(jīng)是指定的大小了。
網(wǎng)絡(luò)編程其實很簡單,任何人都可以寫出一套自己的服務(wù)器框架,但是細(xì)節(jié)決定成敗,性能的高低有時候就是幾個小細(xì)節(jié)決定的(當(dāng)然這里說的這個問題是個編程錯誤,不屬于可優(yōu)化的細(xì)節(jié)問題)