Я использую быстрый Ethernet со скоростью 100 Мбит / с, размер кадра которого менее 1500 байт (1472 байта для полезной нагрузки согласно моему учебнику). При этом я смог отправить и получить пакет UDP размером 65507 байт, что означает, что размер пакета был 65507 + 20 (заголовок IP) + 8 (заголовок UDP) = 65535.
Если размер полезной нагрузки самого кадра составляет максимум 1472 байта (согласно моему учебнику), как размер пакета IP может быть больше, чем тот, который здесь составляет 65535?
Я использовал код отправителя как
char buffer[100000];
for (int i = 1; i < 100000; i++)
{
int len = send (socket_id, buffer, i);
printf("%d\n", len);
}
Код получателя как
while (len = recv (socket_id, buffer, 100000))
{
printf("%d\n". len);
}
Я заметил, что send
returns -1
на i > 65507
и recv
печатает или получает пакет maximum of length 65507
.
Датаграммы UDP не имеют ничего общего с размером MTU, вы можете сделать их сколь угодно большими, но не более 64 КБ, как указано выше. Вы даже можете отправить один из них целым пакетом, если вы используете jumbo-кадры с размером больше большой датаграммы.
Однако большие кадры должны поддерживаться всем оборудованием, через которое они будут передаваться, и это проблема. Для практических целей кадры Ethernet являются наиболее распространенным размером транспортного порта, MTU для них составляет около 1500 байт, я бы сказал 1500 в будущем, но это не всегда. Когда вы создаете дейтаграмму UDP, размер которой превышает базовый MTU (который, как указано, чаще всего является Ethernet), она будет незаметно разбита на несколько фреймов по 1500 байтов. Если вы выполните tcpdump этого трафика, вы увидите несколько пакетов, прерванных на границе MTU, для которых будет установлен флаг большего количества фрагментов вместе с номером фрагмента. Первый пакет будет иметь номер фрагмента 0 и большее количество установленных фрагментов, а последний будет иметь ненулевой номер фрагмента, а другие фрагменты не будут установлены.
Так зачем заботиться? На самом деле важны детали реализации. Фрагментация может снизить производительность в сети, это уже не большая проблема, но о ней следует знать. Если использовалась дейтаграмма огромного размера, то в случае потери какого-либо фрагмента дейтаграммы должны быть отправлены повторно. В равной степени при больших объемах, а сегодня это вполне достижимые объемы, возможно неправильное объединение рамок при повторной сборке. Также могут возникнуть проблемы с получением фрагментированных пакетов UDP для прохождения конфигураций корпоративного брандмауэра, когда балансировщики нагрузки распределяют пакеты, если один фрагмент находится на одном брандмауэре, а другой - на другом, тогда трафик будет отброшен как неполный.
Поэтому не создавайте дейтаграммы UDP, превышающие фрагментацию размера MTU, если вам не нужно и если вам нужно указать, что инфраструктура, между которой осуществляется обмен, близка (такая же подсеть закрыта), и в этот момент jumbo-кадры, вероятно, будут хорошим вариантом.
UDP ничего не знает о MTU. Пакеты UDP могут иметь любой размер от 8 до 65535 байт. Уровни протокола ниже UDP могут либо отправить пакет определенного размера, либо отклонить отправку этого пакета с ошибкой, если он слишком велик.
Уровень ниже UDP обычно IP, IPv4 или IPv6. И IP-пакет может иметь любой размер от 20 (IPv4) / 40 (IPv6) до 65535 байт, что соответствует максимальному размеру UDP. Однако IP поддерживает механизм, называемый фрагментация. Если размер IP-пакета превышает размер, который может передавать нижележащий уровень, IP может разбить один пакет на несколько пакетов, называемых фрагментами. Каждый фрагмент фактически является собственным IP-пакетом (имеет собственный IP-заголовок) и также отправляется отдельно по назначению; Тогда задача пункта назначения состоит в том, чтобы собрать все фрагменты и воссоздать из них полный пакет перед передачей полученных данных на следующий более высокий уровень (например, UDP).
Протокол Ethernet может передавать только кадры с полезной нагрузкой от 46 до 1500 байтов (есть исключения, но это выходит за рамки этого ответа). Если данные полезной нагрузки меньше 46 байтов, они дополняются до 46 байтов. Если данные полезной нагрузки превышают 1500 байт, интерфейс откажется их принять. Если это произойдет, уровень IP теперь должен решить либо фрагментировать пакет, чтобы ни один фрагмент не превышал 1500 байт, либо сообщить об ошибке на следующий более высокий уровень, если фрагментация была отключена или запрещена для этого конкретного соединения.
Обычно следует избегать фрагментации, так как
Вот почему TCP разумно принимает размер своего кадра, чтобы пакетам никогда не требовался IP для их фрагментации. Это можно сделать, запретив IP фрагментировать пакеты, и если IP сообщает, что пакет слишком велик для отправки, TCP уменьшает размер кадра и пытается снова, пока не перестанет сообщаться об ошибке.
Однако для UDP это будет задачей самого приложения, поскольку UDP - это «тупой» протокол, у него нет собственной логики управления, что делает его очень гибким, быстрым и простым.
Единственный размер UDP, на который вы можете положиться, чтобы быть всегда переносимым, составляет 576 минус 8 байтов UDP-заголовка и минус 20 (v4) / 40 (v6) байтов IP-заголовка, поскольку стандарт IP требует, чтобы каждый IP-хост мог получать IP-пакеты с общий размер 576 байт. Реализация вашего протокола не будет соответствовать стандарту, если она не может принимать пакеты, по крайней мере, такого размера. Однако обратите внимание, что стандарт не говорит 576 без фрагментации, поэтому даже 576-байтовый IP-пакет может быть фрагментирован между двумя хостами.
Единственный размер пакета, который вы можете рассчитывать на переносимость без фрагментации, составляет 24 байта для IPv4 и 56 байтов IPv6, поскольку наименьшие IP-заголовки для фрагмента составляют 20/48 байтов (v4 / v6), а фрагмент должен иметь не менее 4/8. байты (v4 / v6) данных полезной нагрузки. Таким образом, транспортная система ниже уровня IP, которая не может транспортировать, по крайней мере, пакеты такого размера, не может использоваться для транспортировки IP-трафика.
И прежде чем кто-либо прокомментирует, что заголовок IPv6 имеет только 40 байтов: это правильно, но, в отличие от заголовка IPv4, стандартный заголовок IPv6 не имеет полей заголовка для фрагментации. Если пакет должен быть фрагментирован, то заголовок расширения фрагментации должен быть добавлен под базовым заголовком IPv6, и этот заголовок расширения имеет длину 8 байтов. Также, в отличие от IPv4, смещения фрагментации в IPv6 подсчитываются по 8 байтов, а не по 4 байта, поэтому в случае IPv6 фрагмент может нести только полезную нагрузку, кратную 8 байтам.
Уровень IP фрагментирует ваш пакет на отправляющей стороне, а затем повторно соберет его на принимающей стороне, прежде чем передать его UDP. На уровне UDP нельзя точно сказать, что пакет был фрагментирован. Если вы используете инструмент захвата пакетов, например Wireshark, вы должны увидеть, что ваш компьютер получает IP-пакеты с ограничением MTU.
Оказывается, разрешение стеку TCP / IP фрагментировать пакеты по мере необходимости - это намного меньше накладных расходов, чем отправка отдельных пакетов.
Чтобы ответить на ваш вопрос: «Если размер полезной нагрузки самого кадра составляет максимум 1472 байта (согласно моему учебнику), как размер пакета IP может быть больше, чем тот, который здесь составляет 65535?»
Это связано с функцией разгрузки, называемой UFO. (UDP Fragmentation Offload). Пожалуйста, обратитесь к этот ссылка на сайт.
Вы можете проверить и переключить функции разгрузки с помощью ethtool -k ethX и ethtool -K ethX соответственно.
Если вы отслеживаете исходящие кадры, возможно, ваш сетевой адаптер поддерживает разгрузку сегментации и включен. При включенной разгрузке сегментации сетевая карта сама обрабатывает сегментирование пакета / кадра на соответствующий размер, а не на сетевой стек. Это освобождает центральный процессор компьютера для выполнения других задач, повышая производительность. В Linux «ethtool -k [устройство]» покажет флаги разгрузки.