簡體   English   中英

什么會導致UDP數據包在發送到本地主機時被丟棄?

[英]What would cause UDP packets to be dropped when being sent to localhost?

我正在發送非常大(64000 字節)的數據報。 我意識到 MTU 遠小於 64000 字節(根據我的閱讀,典型值約為 1500 字節),但我懷疑會發生兩件事之一 - 要么沒有數據報通過(所有大於 1500 字節的數據報)會被悄悄丟棄或導致錯誤/異常被拋出)或者 64000 字節的數據報將被分成大約 43 個 1500 字節的消息並透明傳輸。

從長遠來看(2000+ 64000 字節數據報),大約有 1%(即使對於 LAN 來說似乎異常高)的數據報被丟棄。 我可能希望通過網絡實現這一點,在網絡上,數據報可以無序到達、被丟棄、過濾等等。 但是,我沒想到在本地主機上運行時會出現這種情況。

是什么導致無法在本地發送/接收數據? 我意識到 UDP 不可靠,但我沒想到它在 localhost 上如此不可靠。 我想知道這是否只是一個時間問題,因為發送和接收組件都在同一台機器上。

為了完整起見,我已經包含了發送/接收數據報的代碼。

發送:

DatagramSocket socket = new DatagramSocket(senderPort);

int valueToSend = 0;

while (valueToSend < valuesToSend || valuesToSend == -1) {
    byte[] intBytes = intToBytes(valueToSend);

    byte[] buffer = new byte[bufferSize - 4];

     //this makes sure that the data is put into an array of the size we want to send
    byte[] bytesToSend = concatAll(intBytes, buffer);

    System.out.println("Sending " + valueToSend + " as " + bytesToSend.length + " bytes");

    DatagramPacket packet = new DatagramPacket(bytesToSend,
                        bufferSize, receiverAddress, receiverPort);

    socket.send(packet);

    Thread.sleep(delay);

    valueToSend++;
}

接收:

DatagramSocket socket = new DatagramSocket(receiverPort);

while (true) {
    DatagramPacket packet = new DatagramPacket(
            new byte[bufferSize], bufferSize);

    System.out.println("Waiting for datagram...");
    socket.receive(packet);

    int receivedValue = bytesToInt(packet.getData(), 0);

    System.out.println("Received: " + receivedValue
            + ". Expected: " + expectedValue);

    if (receivedValue == expectedValue) {
        receivedDatagrams++;
        totalDatagrams++;
    }
    else {
        droppedDatagrams++;
        totalDatagrams++;
    }

    expectedValue = receivedValue + 1;
    System.out.println("Expected Datagrams: " + totalDatagrams);
    System.out.println("Received Datagrams: " + receivedDatagrams);
    System.out.println("Dropped Datagrams: " + droppedDatagrams);
    System.out.println("Received: "
            + ((double) receivedDatagrams / totalDatagrams));
    System.out.println("Dropped: "
            + ((double) droppedDatagrams / totalDatagrams));
    System.out.println();
}

概述

是什么導致無法在本地發送/接收數據?

主要是緩沖空間。 想象一下發送恆定的 10MB/秒而只能消耗 5MB/秒。 操作系統和網絡堆棧跟不上,所以數據包被丟棄。 (這與 TCP 不同,TCP 提供流量控制和重傳來處理這種情況。)

即使在沒有溢出緩沖區的情況下消耗數據時,也可能存在無法消耗數據的小時間片,因此系統會丟棄數據包。 (例如在垃圾收集期間,或者當操作系統任務暫時切換到更高優先級的進程時,等等。)

這適用於網絡堆棧中的所有設備。 當隊列已滿時,非本地網絡、以太網交換機、路由器、集線器和其他硬件也會丟棄數據包。 通過 100MB/s 以太網交換機發送 10MB/s 流,而其他人試圖通過同一條物理線路塞滿 100MB/s 會導致數據包丟失。

增加套接字緩沖區大小和操作系統的套接字緩沖區大小。

Linux

默認的套接字緩沖區大小通常128K以下,其中余地很小用於暫停數據處理。

系統控制

使用sysctl增加發送(寫內存 [wmem])和接收(讀內存 [rmem])緩沖區:

  • net.core.wmem_max
  • net.core.wmem_default
  • net.core.rmem_max
  • net.core.rmem_default

例如,要將值增加到 8 兆字節:

sysctl -w net.core.rmem_max=8388608

要使設置保持/etc/sysctl.conf ,請同時更新/etc/sysctl.conf ,例如:

net.core.rmem_max=8388608

一篇關於調整網絡堆棧的深入文章深入探討了更多細節,涉及如何在 Linux 中從內核的網絡驅動程序通過環形緩沖區一直到 C 的recv調用接收和處理數據包的多個級別。 本文介紹了診斷網絡問題時要監控的其他設置和文件。 (見下文。)

在進行以下任何調整之前,請務必了解它們如何影響網絡堆棧。 確實有可能使您的網絡無法使用。 選擇適合您的系統、網絡配置和預期流量負載的數字:

  • net.core.rmem_max=8388608
  • net.core.rmem_default=8388608
  • net.core.wmem_max=8388608
  • net.core.wmem_default=8388608
  • net.ipv4.udp_mem='262144 327680 434274'
  • net.ipv4.udp_rmem_min=16384
  • net.ipv4.udp_wmem_min=16384
  • net.core.netdev_budget=600
  • net.ipv4.ip_early_demux=0
  • net.core.netdev_max_backlog=3000

工具

此外, ethtool可用於查詢或更改網絡設置。 例如,如果${DEVICE}eth0 (使用ip addressipconfig來確定您的網絡設備名稱),那么可以使用以下方法增加 RX 和 TX 緩沖區:

  • ethtool -G ${DEVICE} rx 4096
  • ethtool -G ${DEVICE} tx 4096

iptables

默認情況下, iptables將記錄有關數據包的信息,這會消耗 CPU 時間,盡管很少。 例如,您可以使用以下命令禁用端口 6004 上的 UDP 數據包的日志記錄:

iptables -t raw -I PREROUTING 1 -p udp --dport 6004 -j NOTRACK
iptables -I INPUT 1 -p udp --dport 6004 -j ACCEPT

您的特定端口和協議會有所不同。

監控

幾個文件包含有關在發送和接收的各個階段網絡數據包發生了什么的信息。 在下面的列表中, ${IRQ}是中斷請求號, ${DEVICE}是網絡設備:

  • /proc/cpuinfo - 顯示可用的 CPU 數量(有助於 IRQ 平衡)
  • /proc/irq/${IRQ}/smp-affinity - 顯示 IRQ 關聯
  • /proc/net/dev - 包含一般數據包統計信息
  • /sys/class/net/${DEVICE}/queues/QUEUE/rps_cpus - 與接收數據包控制 (RPS) 有關
  • /proc/softirqs - 用於 ntuple 過濾
  • /proc/net/softnet_stat - 用於數據包統計,例如丟棄、時間擠壓、CPU 沖突等。
  • /proc/sys/net/core/flow_limit_cpu_bitmap - 顯示數據包流(可以幫助診斷大流和小流之間的丟棄)
  • /proc/net/snmp
  • /proc/net/udp

概括

緩沖區空間是丟包的最有可能的罪魁禍首。 網絡堆棧中散布着許多緩沖區,每個緩沖區對發送和接收數據包都有自己的影響。 網絡驅動程序、操作系統、內核設置和其他因素都會影響數據包丟失。 沒有銀彈。

進一步閱讀

UDP pkts 調度可以由操作系統級別的多個線程處理。 這將解釋為什么即使在 127.0.0.1 上您也會無序接收它們。

正如您在問題和對其他答案的眾多評論中所表達的那樣,您的期望是錯誤的。 即使沒有路由器和電纜,以下所有情況也可能發生。

  1. 如果您向任何接收器發送數據包,並且他的套接字接收緩沖區中沒有空間,它將被丟棄。

  2. 如果發送大於路徑 MTU 的 UDP 數據報,它將被分割成更小的數據包,這些數據包受 (1) 約束。

  3. 如果一個數據報的所有數據包都沒有到達,則該數據報將永遠不會被傳送。

  4. TCP/IP 堆棧沒有義務按順序傳送數據包或 UDP 數據報。

UDP 數據包不能保證到達目的地,而 TCP 則可以!

我不知道是什么讓您期望 UDP 的丟包率低於 1%。

話雖如此,基於RFC 1122 (請參閱第 3.3.2 節),保證不會拆分為多個 IP 數據報的最大緩沖區大小為 576 字節。 可以傳輸更大的 UDP 數據報,但它們可能會被拆分為多個 IP 數據報,以便在接收端點重新組裝。

我會想象,一個原因促使你所看到的是,如果這是一個大的UDP數據包的一部分,一個IP數據包丟失,整個UDP數據報將丟失丟包率高 而且您正在計算 UDP 數據報 - 而不是 IP 數據包。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM