一:TCP 和 UDP 之間的區別
↓↓↓
好了,我知道有些童鞋想到我們發過的舊圖了
往期趣圖(點擊下方圖片可跳轉閱讀)
感覺是不是很爽,圖上UDP看起來那麼暴力,但是使用起來感覺還好,內網比較少丟包
二、主要丟包原因
1、接收端處理時間過長導致丟包:調用recv方法接收端收到數據後,處理數據花了一些時間,處理完後再次調用recv方法,在這二次調用間隔裡,發過來的包可能丟失。對於這種情況可以修改接收端,將包接收後存入一個緩衝區,然後迅速返回繼續recv。
2、發送的包巨大丟包:雖然send方法會幫你做大包切割成小包發送的事情,但包太大也不行。例如超過50K的一個udp包,不切割直接通過send方法發送也會導致這個包丟失。這種情況需要切割成小包再逐個send。
3、發送的包較大,超過接受者緩存導致丟包:包超過mtu size數倍,幾個大的udp包可能會超過接收者的緩衝,導致丟包。這種情況可以設置socket接收緩衝。以前遇到過這種問題,我把接收緩衝設置成64K就解決了。
int nRecvBuf=32*1024;//設置為32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
4、發送的包頻率太快:雖然每個包的大小都小於mtu size 但是頻率太快,例如40多個mut size的包連續發送中間不sleep,也有可能導致丟包。這種情況也有時可以通過設置socket接收緩衝解決,但有時解決不了。所以在發送頻率過快的時候還是考慮sleep一下吧。
5、區域網內不丟包,公網上丟包。這個問題我也是通過切割小包並sleep發送解決的。如果流量太大,這個辦法也不靈了。總之udp丟包總是會有的,如果出現了用我的方法解決不了,還有這個幾個方法:要麼減小流量,要麼換tcp協議傳輸,要麼做丟包重傳的工作。
二、具體問題分析
1.發送頻率過高導致丟包
很多人會不理解發送速度過快為什麼會產生丟包,原因就是UDP的SendTo不會造成線程阻塞,也就是說,UDP的SentTo不會像TCP中的SendTo那樣,直到數據完全發送才會return回調用函數,它不保證當執行下一條語句時數據是否被發送。(SendTo方法是異步的)這樣,如果要發送的數據過多或者過大,那麼在緩衝區滿的那個瞬間要發送的報文就很有可能被丟失。至於對「過快」的解釋,作者這樣說:「A few packets a second are not an issue; hundreds or thousands may be an issue.」(一秒鐘幾個數據包不算什麼,但是一秒鐘成百上千的數據包就不好辦了)。要解決接收方丟包的問題很簡單,首先要保證程序執行後馬上開始監聽(如果數據包不確定什麼時候發過來的話),其次,要在收到一個數據包後最短的時間內重新回到監聽狀態,其間要儘量避免複雜的操作(比較好的解決辦法是使用多線程回調機制)。
2.報文過大丟包
至於報文過大的問題,可以通過控制報文大小來解決,使得每個報文的長度小於MTU。乙太網的MTU通常是1500 bytes,其他一些諸如撥號連接的網絡MTU值為1280 bytes,如果使用speaking這樣很難得到MTU的網絡,那麼最好將報文長度控制在1280 bytes以下。
3.發送方丟包
發送方丟包:內部緩衝區(internal buffers)已滿,並且發送速度過快(即發送兩個報文之間的間隔過短); 接收方丟包:Socket未開始監聽; 雖然UDP的報文長度最大可以達到64 kb,但是當報文過大時,穩定性會大大減弱。這是因為當報文過大時會被分割,使得每個分割塊(翻譯可能有誤差,原文是fragmentation)的長度小於MTU,然後分別發送,並在接收方重新組合(reassemble),但是如果其中一個報文丟失,那麼其他已收到的報文都無法返回給程序,也就無法得到完整的數據了。
三、測試TCP,UDP 埠是否通常的小技巧
是不是經常遇見這樣的場景:xx運維:你看看你是不是把伺服器8008埠阿里雲埠沒開,我程序明明跑起來的,在伺服器上能看到埠
運維同學:NND,沒事就說我的問題
上伺服器一看,雲安全策略也是打開,firewall也是埠打開的,咋就不通了呢,這事說不清,甩鍋也甩不清,別慌,把他原來的8008埠進行停止
然後:
python2 -m SimpleHTTPServer 8008telnet xx.xx.xx.xx 8008發現立馬通了,那就直接告訴他代碼的問題吧(一般是一些rpc埠限制了ip)
再來說說UDP,在區塊鏈中,節點與節點之間的通信常常是p2p,會發送UDP命令,但是有的時候發現節點跑不起來,看日誌UDP命令沒發送,那怎麼測試UDP埠是不是通的呢,百度一下,它一般告訴你:
你本地機器一試,果然是通的,確實好用,但是你遠程測試的時候,咋發現提示成功,節點之間還是不發送udp呢?看來光用這還不行,那想一種辦法來監控伺服器是否收到該埠的UDP包了,於是tcpdump上場了