Java отбрасывает половину пакетов UDP

У меня есть простая настройка клиент/сервер. Сервер находится в C, а клиент, который запрашивает сервер, - Java.

моя проблема в том, что, когда я отправляю данные с интенсивной пропускной способностью через соединение, такие как видеокадры, он падает до половины пакетов. Я удостоверяюсь, что я правильно фрагментирую udp-пакеты на стороне сервера (udp имеет максимальную длину полезной нагрузки 2^16). Я проверил, что сервер отправляет пакеты (printf результат sendto ()). Но java, похоже, не получаю половину данных.

кроме того, когда я переключаюсь на TCP, все видеокадры проходят, но задержка начинает нарастать, добавляя несколько секунд задержки после нескольких секунд выполнения.

есть ли что-нибудь очевидное, что я упускаю? Я просто не могу это понять.

5 ответов


получить сетевой инструмент, как Wireshark Так что вы можете видеть, что происходит на проводе.

UDP не делает попыток повторной передачи, поэтому, если пакет где-то отброшен, программа должна справиться с потерей. TCP будет упорно работать, чтобы доставить все пакеты в программу по порядку, отбрасывая dups и запрашивая потерянные пакеты самостоятельно. Если вы видите высокую задержку, я бы поспорил, что вы увидите много потерь пакетов с TCP, которые будут отображаться как повторные передачи от сервера. Если вы не видите повторных передач TCP, возможно, клиент не обрабатывает данные достаточно быстро, чтобы не отставать.


любой протокол приложения на основе UDP неизбежно будет подвержен потере пакетов, переупорядочению и (в некоторых случаях) дубликатам. "U "в UDP может означать" ненадежный", как в ненадежном протоколе дейтаграммы. (Хорошо, это действительно означает "пользователь"... но это, безусловно, хороший способ запомнить характеристики UDP.)

потери пакетов UDP обычно происходят из-за того, что ваш трафик превышает емкость буферизации одного или нескольких "переходов" между сервером и клиентом. Когда это происходит, пакеты отбрасываются ... и поскольку вы используете UDP, нет уведомления на уровне транспортного протокола о том, что это происходит.

Если вы используете UDP в приложении, приложение должно учитывать ненадежный характер UDP, реализуя свои собственные механизмы для работы с отброшенными и неупорядоченными пакетами и для выполнения собственного управления потоком. (Приложение, которое взрывает UDP-пакеты без мысли о том, что это может иметь на уже перегруженная сеть-это плохой водопроводной сети.)

(в случае TCP пакеты, вероятно, также отбрасываются, но TCP обнаруживает и повторно отправляет отброшенные пакеты, и механизм управления потоком TCP включается, чтобы замедлить скорость передачи данных. Конечный результат - "задержка".)

редактировать - основываясь на комментарии OP, причиной его проблемы было то, что клиент не" слушал " в течение некоторого времени, заставляя пакеты (предположительно) отбрасывается ОС клиента. Способ решения этой проблемы заключается в следующем:

  1. используйте выделенный поток Java, который просто считывает пакеты и выстраивает их в очередь для обработки, и

  2. увеличьте размер очереди пакетов ядра для сокета.

но даже когда вы принимаете эти меры, вы все равно можете удалить пакеты. Например, если компьютер перегружен, приложение может не получить время выполнения-срезы достаточно часто, чтобы прочитать и поставить в очередь все пакеты, прежде чем ядро должно их отбросить.

EDIT 2 - есть некоторые споры о том, является ли UDP восприимчивым к дубликатам. Конечно, верно, что UDP не имеет врожденного обнаружения или предотвращения дублирования. Но также верно и то, что структура маршрутизации IP-пакетов, которая является интернетом, вряд ли спонтанно дублирует пакеты. Поэтому дубликаты, если они происходят, могут произойти потому, что отправитель решил отправить UDP пакет. Таким образом, на мой взгляд, хотя UDP восприимчив к проблемам с дубликатами, он их не вызывает per se ... если нет ошибки в стеке протоколов ОС или в IP-структуре.


хотя UDP поддерживает пакеты длиной до 65535 байт (в том числе заголовок UDP, который составляет 8 байт-но см. Примечание 1), базовые транспорты между вами и назначением не поддерживают IP-пакеты, которые долго. Например, кадры Ethernet имеют максимальный размер 1500 байт-принимая во внимание накладные расходы для заголовков IP и UDP, это означает, что любой UDP-пакет с длиной полезной нагрузки данных более 1450, вероятно, будет фрагментирован на несколько IP датаграммы.

максимальный размер UDP-пакета будет фрагментирован по крайней мере на 45 отдельных IP - датаграмм-и если таковые имеются один из этих фрагментов теряется, весь пакет UDP теряется. Если ваша базовая скорость потери пакетов составляет 1%, ваше приложение увидит скорость потери около 36%!

Если вы хотите видеть меньше потерянных пакетов, не отправляйте огромные пакеты-ограничьте свои данные в каждом пакете примерно до 1400 байт (или даже сделайте свое собственное "обнаружение MTU пути", чтобы выяснить максимальный размер вы можете безопасно отправить без фрагментации).


  1. конечно, UDP также подвержен ограничениям IP, а IP-датаграммы имеют максимальный размер 65535, включая IP-заголовок. Размер IP-заголовка колеблется от 20 до 60 байт, поэтому максимальный объем данных приложения, переносимых в UDP-пакете, может достигать 65467.

проблема может быть связана с тем, что ваш буфер передачи заполняется в вашем UDPSocket. Только отправьте количество байтов за один раз, указанное UDPSocket.getSendBufferSize(). Использовать setSendBufferSize(int size) для увеличения этого значения.

Если #send () используется для отправки DatagramPacket, который больше, чем настройка SO_SNDBUF тогда это конкретная реализация, если пакет отправляется или отбрасывается.


IP поддерживает пакетов до 65535 байт, включая 20 заголовок пакета IP байта. UDP поддерживает дейтаграммы до 65507 байт, плюс 20-байтовый IP-заголовок и 8 заголовок UDP байта. Однако сетевой MTU-это практический предел, и не забывайте, что он включает не только эти 28 байтов, но и заголовок фрейма Ethernet. The реальные практически предел для unfragmented UDP минимум MTU 576 байт меньше всех накладных расходов.