web-dev-qa-db-ja.com

ローカルホストに送信されるときにUDPパケットがドロップされる原因は何ですか?

非常に大きな(64000バイト)データグラムを送信しています。私はMTUが64000バイトよりはるかに小さいことを認識しています(通常の値は私の読み取り値から約1500バイトです)。2つのうちの1つが発生すると思われます-データグラムが通過しない(1500バイトより大きいすべて)サイレントにドロップされるか、エラー/例外がスローされます)、または64000バイトのデータグラムが約43の1500バイトのメッセージにチャンク化され、透過的に送信されます。

長期にわたって(2000+ 64000バイトのデータグラム)、データグラムの約1%(LANでも異常に高いようです)がドロップされます。これは、データグラムが順不同で到着したり、ドロップされたり、フィルタリングされたりする可能性があるネットワークを介してこれを予期する場合があります。ただし、localhostで実行しているときは、これは予期していませんでした。

ローカルでデータを送受信できない原因は何ですか? UDPは信頼できないことに気づきましたが、ローカルホストではUDPがそれほど信頼できないとは思っていませんでした。送信コンポーネントと受信コンポーネントの両方が同じマシン上にあるので、それは単なるタイミングの問題なのかと思います。

完全を期すために、データグラムを送受信するためのコードを含めました。

送信:

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();
}
22
Thomas Owens

概観

ローカルでデータを送受信できない原因は何ですか?

主にバッファ領域。 5MB /秒しか消費できないが、一定の10MB /秒を送信することを想像してください。オペレーティングシステムとネットワークスタックが追いつかないため、パケットがドロップされます。 (これは、そのような状況を処理するためにフロー制御と再送信を提供するTCPとは異なります。)

バッファがオーバーフローすることなくデータが消費される場合でも、データを消費できない小さなタイムスライスが存在する可能性があるため、システムはパケットをドロップします。 (ガベージコレクション中、またはOSタスクが一時的に優先度の高いプロセスに切り替わるときなど)。

これは、ネットワークスタック内のすべてのデバイスに適用されます。非ローカルネットワーク、イーサネットスイッチ、ルーター、ハブ、およびその他のハードウェアも、キューがいっぱいになるとパケットをドロップします。 100MB/sイーサネットスイッチを介して10MB/sストリームを送信している間に、他の誰かが同じ物理回線を介して100MB/sを詰め込もうとすると、パケットがドロップされます。

ソケットバッファサイズ とオペレーティングシステムのソケットバッファサイズの両方を増やします。

Linux

デフォルトのソケットバッファーサイズは通常128k以下であり、データ処理を一時停止するためのスペースが非常に残ります。

sysctl

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も更新します。

net.core.rmem_max=8388608

ネットワークスタックのチューニングに関する 詳細な記事 は、カーネルのネットワークドライバーからCのrecvまでのすべての方法で、カーネルのネットワークドライバーからLinuxでパケットが受信および処理される方法の複数のレベルに触れ、さらに詳細に掘り下げていますコール。この記事では、ネットワークの問題を診断するときに監視する追加の設定とファイルについて説明します。 (下記参照。)

以下の微調整を行う前に、それらがネットワークスタックにどのように影響するかを必ず理解してください。 ネットワークを使用不能にする可能性が実際にあります。システム、ネットワーク構成、および予想されるトラフィック負荷に適した数値を選択してください。

  • 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

さらに、ethtoolは、ネットワーク設定のクエリまたは変更に役立ちます。たとえば、${DEVICE}eth0である場合(ip addressまたはipconfigを使用して、ネットワークデバイス名を決定します)、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

概要

ドロップされたパケットの最も可能性の高い原因はバッファスペースです。ネットワークスタック全体に多数のバッファが散在しており、それぞれがパケットの送受信に独自の影響を与えます。ネットワークドライバー、オペレーティングシステム、カーネル設定、およびその他の要因がパケットドロップに影響を与える可能性があります。特効薬はありません。

参考文献

28
nos

UDP pktsスケジューリングは、OSレベルの複数のスレッドによって処理される場合があります。これは、127.0.0.1でも順不同で受け取る理由を説明しています。

4
Niko

質問や他の回答に対する多数のコメントで表明されている期待は間違っています。ルーターとケーブルがない場合でも、以下のすべてが発生する可能性があります。

  1. パケットをいずれかのレシーバーに送信し、ソケットの受信バッファーにスペースがない場合は、ドロップされます。

  2. パスMTUより大きいUDPデータグラムを送信すると、(1)の影響を受ける小さなパケットにフラグメント化されます。

  3. データグラムのすべてのパケットが到着しない場合、データグラムは配信されません。

  4. TCP/IPスタックには、パケットまたはUDPデータグラムを順番に配信する義務はありません。

1
user207421

UDPでドロップされたパケットのパーセンテージが1%未満であると予想される理由はわかりません。

つまり、 RFC 1122 に基づいて( セクション3.3.2 を参照)、複数のIPデータグラムに分割されないことが保証されている最大バッファーサイズは576バイトです。より大きなUDPデータグラムが送信される可能性がありますが、受信エンドポイントで再構成されるために複数のIPデータグラムに分割される可能性があります。

ドロップされたパケットのhighレートに寄与する理由は、大きなUDPデータグラムの一部であった1つのIPパケットが失われると、UDP全体が失われることだと思いますデータグラムは失われます。そして、IPパケットではなく、UDPデータグラムをカウントしています。

0
Mike Dinescu

UDPパケットは宛先に到達することが保証されていませんが、TCPはそうです!

0
r0ast3d