<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

接收UDP報文的過程

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-11 11:22 ? 次閱讀

最近工作中遇到某個服務器應用程序 UDP 丟包,在排查過程中查閱了很多資料,總結出來這篇文章,供更多人參考。

在開始之前,我們先用一張圖解釋 linux 系統接收網絡報文的過程。

  1. 首先網絡報文通過物理網線發送到網卡
  2. 網絡驅動程序會把網絡中的報文讀出來放到 ring buffer 中,這個過程使用 DMA(Direct Memory Access),不需要 CPU 參與
  3. 內核從 ring buffer 中讀取報文進行處理,執行 IP 和 TCP/UDP 層的邏輯,最后把報文放到應用程序的 socket buffer 中
  4. 應用程序從 socket buffer 中讀取報文進行處理

圖片

在接收 UDP 報文的過程中,圖中任何一個過程都可能會主動或者被動地把報文丟棄,因此丟包可能發生在網卡和驅動,也可能發生在系統和應用。

之所以沒有分析發送數據流程,一是因為發送流程和接收類似,只是方向相反;另外發送流程報文丟失的概率比接收小,只有在應用程序發送的報文速率大于內核和網卡處理速率時才會發生。

本篇文章假定機器只有一個名字為 eth0 的 interface,如果有多個 interface 或者 interface 的名字不是 eth0,請按照實際情況進行分析。

NOTE:文中出現的 RX(receive) 表示接收報文,TX(transmit) 表示發送報文。

確認有 UDP 丟包發生

要查看網卡是否有丟包,可以使用 ethtool -S eth0 查看,在輸出中查找 bad 或者 drop 對應的字段是否有數據,在正常情況下,這些字段對應的數字應該都是 0。如果看到對應的數字在不斷增長,就說明網卡有丟包。

另外一個查看網卡丟包數據的命令是 ifconfig,它的輸出中會有 RX(receive 接收報文)和 TX(transmit 發送報文)的統計數據:

~# ifconfig eth0
...
        RX packets 3553389376  bytes 2599862532475 (2.3 TiB)
        RX errors 0  dropped 1353  overruns 0  frame 0
        TX packets 3479495131  bytes 3205366800850 (2.9 TiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
...

此外,linux 系統也提供了各個網絡協議的丟包信息,可以使用 netstat -s 命令查看,加上 --udp 可以只看 UDP 相關的報文數據:

[root@holodesk02 GOD]# netstat -s -u
IcmpMsg:
    InType0: 3
    InType3: 1719356
    InType8: 13
    InType11: 59
    OutType0: 13
    OutType3: 1737641
    OutType8: 10
    OutType11: 263
Udp:
    517488890 packets received
    2487375 packets to unknown port received.
    47533568 packet receive errors
    147264581 packets sent
    12851135 receive buffer errors
    0 send buffer errors
UdpLite:
IpExt:
    OutMcastPkts: 696
    InBcastPkts: 2373968
    InOctets: 4954097451540
    OutOctets: 5538322535160
    OutMcastOctets: 79632
    InBcastOctets: 934783053
    InNoECTPkts: 5584838675

對于上面的輸出,關注下面的信息來查看 UDP 丟包的情況:

  • packet receive errors 不為空,并且在一直增長說明系統有 UDP 丟包
  • packets to unknown port received 表示系統接收到的 UDP 報文所在的目標端口沒有應用在監聽,一般是服務沒有啟動導致的,并不會造成嚴重的問題
  • receive buffer errors 表示因為 UDP 的接收緩存太小導致丟包的數量

NOTE:并不是丟包數量不為零就有問題,對于 UDP 來說,如果有少量的丟包很可能是預期的行為,比如丟包率(丟包數量/接收報文數量)在萬分之一甚至更低。

網卡或者驅動丟包

之前講過,如果 ethtool -S eth0 中有 rx_***_errors 那么很可能是網卡有問題,導致系統丟包,需要聯系服務器或者網卡供應商進行處理。

# ethtool -S eth0 | grep rx_ | grep errors
     rx_crc_errors: 0
     rx_missed_errors: 0
     rx_long_length_errors: 0
     rx_short_length_errors: 0
     rx_align_errors: 0
     rx_errors: 0
     rx_length_errors: 0
     rx_over_errors: 0
     rx_frame_errors: 0
     rx_fifo_errors: 0

netstat -i 也會提供每個網卡的接發報文以及丟包的情況,正常情況下輸出中 error 或者 drop 應該為 0。

如果硬件或者驅動沒有問題,一般網卡丟包是因為設置的緩存區(ring buffer)太小,可以使用 ethtool 命令查看和設置網卡的 ring buffer。

ethtool -g 可以查看某個網卡的 ring buffer,比如下面的例子

# ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:        4096
RX Mini:    0
RX Jumbo:    0
TX:        4096
Current hardware settings:
RX:        256
RX Mini:    0
RX Jumbo:    0
TX:        256

Pre-set 表示網卡最大的 ring buffer 值,可以使用 ethtool -G eth0 rx 8192 設置它的值。

Linux 系統丟包

linux 系統丟包的原因很多,常見的有:UDP 報文錯誤、防火墻、UDP buffer size 不足、系統負載過高等,這里對這些丟包原因進行分析。

UDP 報文錯誤

如果在傳輸過程中UDP 報文被修改,會導致 checksum 錯誤,或者長度錯誤,linux 在接收到 UDP 報文時會對此進行校驗,一旦發明錯誤會把報文丟棄。

如果希望 UDP 報文 checksum 及時有錯也要發送給應用程序,可以在通過 socket 參數禁用 UDP checksum 檢查:

int disable = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_NO_CHECK, (void*)&disable, sizeof(disable)

防火墻

如果系統防火墻丟包,表現的行為一般是所有的 UDP 報文都無法正常接收,當然不排除防火墻只 drop 一部分報文的可能性。

如果遇到丟包比率非常大的情況,請先檢查防火墻規則,保證防火墻沒有主動 drop UDP 報文。

UDP buffer size 不足

linux 系統在接收報文之后,會把報文保存到緩存區中。因為緩存區的大小是有限的,如果出現 UDP 報文過大(超過緩存區大小或者 MTU 大?。?、接收到報文的速率太快,都可能導致 linux 因為緩存滿而直接丟包的情況。

在系統層面,linux 設置了 receive buffer 可以配置的最大值,可以在下面的文件中查看,一般是 linux 在啟動的時候會根據內存大小設置一個初始值。

  • /proc/sys/net/core/rmem_max:允許設置的 receive buffer 最大值
  • /proc/sys/net/core/rmem_default:默認使用的 receive buffer 值
  • /proc/sys/net/core/wmem_max:允許設置的 send buffer 最大值
  • /proc/sys/net/core/wmem_dafault:默認使用的 send buffer 最大值

但是這些初始值并不是為了應對大流量的 UDP 報文,如果應用程序接收和發送 UDP 報文非常多,需要講這個值調大??梢允褂?sysctl 命令讓它立即生效:

sysctl -w net.core.rmem_max=26214400 # 設置為 25M

也可以修改 /etc/sysctl.conf 中對應的參數在下次啟動時讓參數保持生效。

如果報文報文過大,可以在發送方對數據進行分割,保證每個報文的大小在 MTU 內。

另外一個可以配置的參數是 netdev_max_backlog,它表示 linux 內核從網卡驅動中讀取報文后可以緩存的報文數量,默認是 1000,可以調大這個值,比如設置成 2000:

sudo sysctl -w net.core.netdev_max_backlog=2000

系統負載過高

系統 CPU、memory、IO 負載過高都有可能導致網絡丟包,比如 CPU 如果負載過高,系統沒有時間進行報文的 checksum 計算、復制內存等操作,從而導致網卡或者 socket buffer 出丟包;memory 負載過高,會應用程序處理過慢,無法及時處理報文;IO 負載過高,CPU 都用來響應 IO wait,沒有時間處理緩存中的 UDP 報文。

linux 系統本身就是相互關聯的系統,任何一個組件出現問題都有可能影響到其他組件的正常運行。對于系統負載過高,要么是應用程序有問題,要么是系統不足。對于前者需要及時發現,debug 和修復;對于后者,也要及時發現并擴容。

應用丟包

上面提到系統的 UDP buffer size,調節的 sysctl 參數只是系統允許的最大值,每個應用程序在創建 socket 時需要設置自己 socket buffer size 的值。

linux 系統會把接受到的報文放到 socket 的 buffer 中,應用程序從 buffer 中不斷地讀取報文。所以這里有兩個和應用有關的因素會影響是否會丟包:socket buffer size 大小以及應用程序讀取報文的速度。

對于第一個問題,可以在應用程序初始化 socket 的時候設置 socket receive buffer 的大小,比如下面的代碼把 socket buffer 設置為 20MB:

uint64_t receive_buf_size = 20*1024*1024;  //20 MB
setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &receive_buf_size, sizeof(receive_buf_size));

如果不是自己編寫和維護的程序,修改應用代碼是件不好甚至不太可能的事情。很多應用程序會提供配置參數來調節這個值,請參考對應的官方文檔;如果沒有可用的配置參數,只能給程序的開發者提 issue 了。

很明顯,增加應用的 receive buffer 會減少丟包的可能性,但同時會導致應用使用更多的內存,所以需要謹慎使用。

另外一個因素是應用讀取 buffer 中報文的速度,對于應用程序來說,處理報文應該采取異步的方式

包丟在什么地方

想要詳細了解 linux 系統在執行哪個函數時丟包的話,可以使用 dropwatch 工具,它監聽系統丟包信息,并打印出丟包發生的函數地址:

# dropwatch -l kas
Initalizing kallsyms db
dropwatch > start
Enabling monitoring...
Kernel monitoring activated.
Issue Ctrl-C to stop monitoring

1 drops at tcp_v4_do_rcv+cd (0xffffffff81799bad)
10 drops at tcp_v4_rcv+80 (0xffffffff8179a620)
1 drops at sk_stream_kill_queues+57 (0xffffffff81729ca7)
4 drops at unix_release_sock+20e (0xffffffff817dc94e)
1 drops at igmp_rcv+e1 (0xffffffff817b4c41)
1 drops at igmp_rcv+e1 (0xffffffff817b4c41)

通過這些信息,找到對應的內核代碼處,就能知道內核在哪個步驟中把報文丟棄,以及大致的丟包原因。

此外,還可以使用 linux perf 工具監聽 kfree_skb(把網絡報文丟棄時會調用該函數) 事件的發生:

sudo perf record -g -a -e skb:kfree_skb
sudo perf script

關于 perf 命令的使用和解讀,網上有很多文章可以參考。

總結

  • UDP 本身就是無連接不可靠的協議,適用于報文偶爾丟失也不影響程序狀態的場景,比如視頻、音頻、游戲、監控等。對報文可靠性要求比較高的應用不要使用 UDP,推薦直接使用 TCP。當然,也可以在應用層做重試、去重保證可靠性
  • 如果發現服務器丟包,首先通過監控查看系統負載是否過高,先想辦法把負載降低再看丟包問題是否消失
  • 如果系統負載過高,UDP 丟包是沒有有效解決方案的。如果是應用異常導致 CPU、memory、IO 過高,請及時定位異常應用并修復;如果是資源不夠,監控應該能及時發現并快速擴容
  • 對于系統大量接收或者發送 UDP 報文的,可以通過調節系統和程序的 socket buffer size 來降低丟包的概率
  • 應用程序在處理 UDP 報文時,要采用異步方式,在兩次接收報文之間不要有太多的處理邏輯
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11022

    瀏覽量

    207066
  • 服務器
    +關注

    關注

    12

    文章

    8271

    瀏覽量

    83065
  • UDP
    UDP
    +關注

    關注

    0

    文章

    311

    瀏覽量

    33689
  • 報文
    +關注

    關注

    0

    文章

    31

    瀏覽量

    3991
收藏 人收藏

    評論

    相關推薦

    UDP有發送緩存區嗎?如何解決UDP丟包的問題呢?

    每個 UDP 報文分為 UDP 報頭和 UDP 數據區兩部分。報頭由 4 個 16 位長(2 字節)字段組成,分別說明該報文的源端口、目的端
    的頭像 發表于 08-15 09:33 ?6197次閱讀
    <b class='flag-5'>UDP</b>有發送緩存區嗎?如何解決<b class='flag-5'>UDP</b>丟包的問題呢?

    通信必備知識!TCP與UDP協議介紹及使用

    的協議,它在數據傳輸之前不需要建立連接。發送端可以直接將數據報文(數據段)扔到網絡上,而接收端則從網絡中接收數據,并從消息隊列中讀取數據段。UDP不提供可靠性和順序
    的頭像 發表于 03-15 08:19 ?1134次閱讀
    通信必備知識!TCP與<b class='flag-5'>UDP</b>協議介紹及使用

    請問STM32F4通過W5500能不能得到網口的所有UDP和TCP報文?

    網口攝像頭連到路由,一般都是先UDP廣播,有回應后,然后連接到目標IP端口,將攝像的數據流上傳,,,我現在想做一個取代路由的模塊,通過無線連接到目標IP上傳數據流,但是現在沒辦法接收攝像頭發送的任何報文,有沒有大神指導一下,給我
    發表于 04-25 06:29

    ESP32C6 WiFi報文出現大量重傳是什么原因導致的?

    使用ESP32C6作為AP與另一設備通信,傳輸層使用UDP協議,C6每隔100ms會發送一幀UDP報文,通過wireshark捕獲報文發現,每發送一幀
    發表于 06-06 07:55

    ESP32C6作為UDP Server,使用recvfrom無法及時收到第一幀報文的原因?如何解決?

    我使用esp32-c6作為WiFi AP,當有STA接入且通過DHCP為其分配了IP地址后,AP會創建一個udp socket作為server等待接收來自客戶端的UDP報文,AP成功創
    發表于 06-06 07:34

    CAN總線ID 報文接收問題

    CAN報文接收時MASK的值為0時 不管ID是多少 都接收MASK為1時看發送節點ID與驗收碼是否一致同一位上同是0或者同是1則接收,否則不接收
    發表于 01-02 20:04

    如何關閉UDP接收時的校驗?

    昨天發帖問了一下UDP接收的問題,今天發現可能是UDP校驗的原因,因為從機送過來的數據沒有校驗:ethernet frame check sequence incorrect。用網絡調試助手能收到并
    發表于 05-03 10:24

    如何解決LWIP中UDP數據收發文件接收的問題?

    申請不到足夠的pbuf去容納數據,所以也就進不了UDP接收回調函數了,可是一個數據幀頂多也就1472字節,理論上應該跟TCP接受過程一樣,為何會一下子吧pbuf耗盡了?如此說來要是我UDP
    發表于 07-16 04:35

    關于W5500芯片UDP發送報文到不同IP的問題

    同樣的報文,每個socket每秒發送1次。問題描述如下:1、3個電腦都連接網線時,接收UDP報文均正常;2、只有2個電腦連接網線時,每秒時,期望每臺電腦只收到1組
    發表于 09-02 15:24

    教你動手寫UDP協議?!狣NS報文解析

    教你動手寫UDP協議棧系列文章序號內容1《教你動手寫UDP協議棧-UDP協議棧格式》2《教你動手寫UDP協議棧-DHCP報文解析》3《教你動
    的頭像 發表于 12-24 16:16 ?1161次閱讀

    UDP理論講解

    UDP報文成為用戶數據報,用戶數據報的結構分為兩部分:UDP首部+UDP數據區,如下圖為UDP報文
    的頭像 發表于 08-13 09:47 ?1274次閱讀

    UDP分片與丟包,UDP真的比TCP高效嗎?

    UDP:當套接口接收緩沖區滿時,新來的數據報無法進入接收緩沖區,此數據報就被丟棄。UDP是沒有流量控制的;快的發送者可以很容易地就淹沒慢的接收
    的頭像 發表于 12-21 11:12 ?970次閱讀

    UDP協議的報文格式

    UDP用來支持那些需要在計算機之間傳輸數據的網絡應用。包括網絡視頻會議系統在內的眾多的客戶/服務器模式的網絡應用都需要使用UDP協議。
    發表于 05-06 15:26 ?2831次閱讀
    <b class='flag-5'>UDP</b>協議的<b class='flag-5'>報文</b>格式

    簡述linux系統UDP丟包問題分析思路(上)

    在開始之前,我們先用一張圖解釋 linux 系統接收網絡報文過程。 1. 首先網絡報文通過物理網線發送到網卡 2. 網絡驅動程序會把網絡中的
    的頭像 發表于 05-18 17:24 ?2405次閱讀
    簡述linux系統<b class='flag-5'>UDP</b>丟包問題分析思路(上)

    簡述linux系統UDP丟包問題分析思路(下)

    在開始之前,我們先用一張圖解釋 linux 系統接收網絡報文過程。 1. 首先網絡報文通過物理網線發送到網卡 2. 網絡驅動程序會把網絡中的
    的頭像 發表于 05-18 17:25 ?1211次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>