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