主要丟包緣由
一、接收端處理時間過長致使丟包:調用recv方法接收端收到數據後,處理數據消耗一些時間,處理完後再次調用recv方法,在這
二次調用間隔裏,發過來的包可能丟失。對於這種狀況能夠修改接收端,將包接收後存入一個緩衝區,而後迅速返回繼續recv。
二、發送的包巨大丟包:雖然send方法會幫你作大包切割成小包發送的事情,但包太大也不行。例如超過50K的一個udp包,不切割
直接經過send方法發送也會致使這個包丟失。這種狀況須要切割成小包再逐個send。
三、發送的包較大,超過接受者緩存致使丟包:包超過mtu size數倍,幾個大的udp包可能會超過接收者的緩衝,致使丟包。這種
狀況能夠設置socket接收緩衝。之前遇到過這種問題,我把接收緩衝設置成64K就解決了。
int nRecvBuf=32*1024;//設置爲32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
四、發送的包頻率太快:雖然每一個包的大小都小於mtu size 可是頻率太快,例如40多個mut size的包連續發送中間不sleep,也有
可能致使丟包。這種狀況也有時能夠經過設置socket接收緩衝解決,但有時解決不了。因此在發送頻率過快的時候仍是考慮sleep
一下吧。
五、局域網內不丟包,公網上丟包。這個問題可經過切割小包並sleep發送解決。若是流量太大,這個辦法也不靈了。
總之,udp丟包老是不可避免的,如上述方法還沒法解決: 要麼減少流量,要麼換tcp協議傳輸,要麼作丟包重傳的工做。
具體問題分析
發送頻率太高致使丟包
不少人會不理解發送速度過快爲何會產生丟包,緣由就是UDP的SendTo不會形成線程阻塞,也就是說,UDP的SentTo不會像TCP中
的SendTo那樣,直到數據徹底發送纔會return回調用函數,它不保證當執行下一條語句時數據是否被髮送。(SendTo方法是異步
的)這樣,若是要發送的數據 過多或者過大,那麼在緩衝區滿的那個瞬間要發送的報文就頗有可能被丟失。至於對「過快」的解釋,
做者這樣說:「A few packets a second are not an issue; hundreds or thousands may be an issue.」(一秒鐘幾個數據
包不算什麼,可是一秒鐘成百上千的數據包就很差辦了)。 要解決接收方丟包的問題很簡單,首先要保證程序執行後立刻開始監聽
(若是數據包不肯定何時發過來的話),其次,要在收到一個數據包後最短的時間內從新回到監聽狀態,其間要儘可能避免複雜的
操做(比較好的解決辦法是使用多線程回調機制)。
報文過大丟包
至於報文過大的問題,能夠經過控制報文大小來解決,使得每一個報文的長度小於MTU。以太網的MTU一般是1500 bytes,其餘一些
諸如撥號鏈接的網絡MTU值爲1280 bytes,若是使用speaking這樣很可貴到MTU的網絡,那麼最好將報文長度控制在1280 bytes以
下。
發送方丟包
發送方丟包:內部緩衝區(internal buffers)已滿,而且發送速度過快(即發送兩個報文之間的間隔太短);
接收方丟包:Socket未開始監聽; 雖然UDP的報文長度最大能夠達到64 kb,可是當報文過大時,穩定性會大大減弱。
這是由於當報文過大時會被分割,使得每一個fragmentation(分割包)的長度小於MTU,而後分別發送,並在接收方從新
組合(reassemble),可是若是其中一個報文丟失,那麼其餘已收到的報文都沒法返回給程序,也就沒法獲得完整的數據了。