TCP UDP丟包常見問題小結 增加 udp快取

2021-08-26 12:21:11 字數 2957 閱讀 8168

1,udp丟包

困擾幾天的udp內網傳輸部分終於做通了,解決的關鍵就在於setsockopt的呼叫,設定接收緩衝。

遇到的問題是這樣的,主機端傳送udp資料報:

應用層的包大小為1452byte大小,這樣拆包是根據乙太網的mtu為1500位元組而考慮的(當然外網狀態下並不一定就是乙太網網路,路由mtu可能更加小),因為在網路層和傳輸層還有8byte的udp包頭和20byte的ip包頭,所以乙太網幀大小為1452+8+20 = 1480byte。

int n = 512*1024;  setsockopt(m_hrcvsock, sol_socket, so_rcvbuf, (const char*)&n, sizeof(n));

說到流量控制,不得不提到tcp的另乙個重要概念-—視窗。視窗表示了接收主機能接收的最大資料量,並且,視窗大小是隨著主機資源和主機當前正在接收多少個傳輸數量而變化的。主機將視窗字段用於流量控制,也就是說,流量控制是tcp視窗的乙個功能。tcp採用流量控制管理進入接收主機緩衝區的資料流量。如果傳送主機傳輸資料的速度比接收主機處理資料的速度更快以至接收主機緩衝區已滿不能處理更多的資料時,則接收主機就會請求傳送主機降低資料傳送速度直到接收主機可以接收更多的資料為止;相反,如果接收主機能夠處理更多的資料,則會請求傳送主機加快資料的傳送速度,這就是流量控制的用途,它保證了資料在傳輸的過程中完整的傳送到接收主機。

可以看出由於tcp是基於連線的,所以其在傳輸過程中會犧牲很多來進行傳輸的保證,故即使速度下降也會保證接收端的有序和正確接收。而udp是非連線的,其傳送後不進行任何處理在保證資料的傳輸,高效但無保障,一切檢驗和有序以及完整處理均需要應用層來完成(這讓人想起了rtcp)。

2,繫結失敗

還有乙個setsockopt的選相是so_reuseaddr, 今天在繫結乙個位址來進行偵聽的時候,處理上是每來乙個連線,便偵聽其位址傳送來的udp埠,由於要多次繫結,而開始時總遇到位址重複的錯誤,後來一查發現,同乙個位址進行埠繫結,好像有乙個時間間隔限制,該限制以內不能重複繫結,故我進行了以上的設定,就可以多次重複繫結了~

使用 bind api 函式來繫結乙個位址(乙個介面和乙個埠)到乙個套接字端點。可以在伺服器設定中使用這個函式,以便限制可能有連線到來的介面。也可以在客戶端設定中使用這個函式,以便限制應當供出去的連線所使用的介面。bind最常見的用法是關聯埠號和伺服器,並使用萬用字元位址(inaddr_any),它允許任何介面為到來的連線所使用。bind 普遍遭遇的問題是試圖繫結乙個已經在使用的埠。該陷阱是也許沒有活動的套接字存在,但仍然禁止繫結埠(bind 返回eaddrinuse),它由 tcp 套接字狀態 time_wait 引起。該狀態在套接字關閉後約保留 2 到 4 分鐘。在 time_wait 狀態退出之後,套接字被刪除,該位址才能被重新繫結而不出問題。

等待 time_wait 結束可能是令人惱火的一件事,特別是如果您正在開發乙個套接字伺服器,就需要停止伺服器來做一些改動,然後重啟。幸運的是,有方法可以避開 time_wait 狀態。可以給套接字應用 so_reuseaddr 套接字選項,以便埠可以馬上重用。

同樣,我在每次udp偵聽socket使用完畢後,使用closesocket將使用的socket清除,這樣手動釋放後,不需要再進行setsockopt的設定也可以重複繫結了。從此可看出,原本我建立的socket為區域性的,但其釋放好像並不同與普通的c變數的釋放方式,故在函式下次呼叫時候,出現位址重複的錯誤,而手動清除是保險的。該錯誤在linux和windows平台均有,但好像windows再呼叫setsockopt後,socket接收有異常,偶爾接收不了udp報文,而linux下沒有此種現象。

什麼會導致udp丟包呢,我這裡列舉了如下幾點原因:

1.呼叫recv方法接收端收到資料後,處理資料花了一些時間,處理完後再次呼叫recv方法,在這二次呼叫間隔裡,發過來的包可能丟失。對於這種情況可以修改接收端,將包接收後存入乙個緩衝區,然後迅速返回繼續recv。

2.傳送的包巨大丟包。雖然send方法會幫你做大包切割成小包傳送的事情,但包太大也不行。例如超過30k的乙個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接收緩衝解決,但有時解決不了。

5.傳送的廣播包或組播包在windws和linux下都接收正常,而arm上接收出現丟包。這個還不好解決,我的解決方法是大包切割成大小為1448的小包傳送,每個包之間sleep 1毫秒,雖然笨,但有效。我這裡mtu size為1500位元組,減去udp包頭8個位元組,減去傳輸層幾十個位元組,實際資料位1448位元組。

除此之外還可以試試設定arm作業系統緩衝:

//設定mtu size 1500最大

ifconfig eth0 mtu 1500

//檢視接收緩衝最大和預設大小。

sysctl -a | grep rmem

//設定接收緩衝的最大大小

sysctl -w net.core.rmem_max=1048576

sysctl -w net.core.rmem_default=1048576

sysctl -w net.ipv4.udp_mem=1048576

sysctl -w net.ipv4.udp_rmem_min=1048576

6,區域網內不丟包,公網上丟包。這個問題我也是通過切割小包並sleep傳送解決的。如果流量太大,這個辦法也不靈了。

總之udp丟包總是會有的,如果出現了用我的方法解決不了,還有這個幾個方法: 要麼減小流量,要麼換tcp協議傳輸,要麼做丟包重傳的工作

TCP UDP常見問題小結

1,udp丟包 困擾幾天的udp內網傳輸部分終於做通了,解決的關鍵就在於setsockopt的呼叫,設定接收緩衝。遇到的問題是這樣的,主機端傳送udp資料報 應用層的包大小為1452byte大小,這樣拆包是根據乙太網的mtu為1500位元組而考慮的 當然外網狀態下並不一定就是乙太網網路,路由mtu可...

TCP UDP常見問題合集

沒有圖,都是口述,簡答或面試時使用。1.源埠號一般是大於1024的隨機埠 埠號範圍 0 65535 2.常用的tcp udp埠號 tcp 20,21 ftp 20為資料埠,21為命令埠 udp 67 68 dhcp 68源埠,67目的埠 udp 161 162 snmp 161收發請求,162接收t...

常見問題小結

二 tips 實際也是一種塌陷,運用bfc,給元素新增overflow hidden 構建成另乙個bfc來處理 doctype html en utf 8 title box block1 block2 style head box block1 div box block2 內容 隱藏 displ...