Можно ли разбить пакеты TCP и UDP на части?

41

Могут ли TCP-пакеты приходить к получателю по частям?

Например, если я отправляю 20 байтов по протоколу TCP, могу ли я быть на 100% уверен, что получу ровно 20 байтов одновременно, а не 10 байтов, тогда еще 10 байтов или около того?

И тот же вопрос для протокола UDP.
Я знаю, что UDP ненадежен, и пакеты не могут прийти вообще или прийти в другом порядке, но как насчет одного пакета? Если он прибудет, могу ли я быть уверен, что это полный пакет, а не кусок?

iamnp
источник
7
Уточнение: это называется сегмент TCP и дейтаграмма UDP. Они не пакеты. TCP = сегмент, UDP = датаграмма, IP = пакет, Ethernet = кадр, на всех других уровнях (AFAIK) они просто называются PDU (единицы данных протокола).
Joeqwerty

Ответы:

33

TCP-пакеты могут поступать получателю по частям?

Да. IP поддерживает фрагментацию, хотя TCP обычно пытается определить MTU пути и сохранить свои пакеты меньшими, чем по соображениям производительности. Фрагментация катастрофически увеличивает скорость потери дейтаграмм. Если путь имеет 10% -ную потерю пакетов, фрагментация дейтаграммы на два пакета делает потерю дейтаграммы почти 20%. (Если какой-либо пакет потерян, датаграмма потеряна.)

Вам не нужно беспокоиться об этом, как и уровень TCP. Уровень IP собирает пакеты в целые дейтаграммы.

Например: если я отправлю 20 байтов по протоколу TCP, могу ли я быть на 100% уверен, что получу ровно 20 байтов одновременно, а не 10 байтов, тогда еще 10 байтов или около того?

Нет, но это не имеет ничего общего с пакетами. По сути, TCP является протоколом потока байтов, который не сохраняет границы сообщений приложения.

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

То же самое верно и для TCP. Пакеты - это пакеты. Разница в том, что в протоколе TCP есть повторы и переупорядочение, встроенные в протокол, а в протоколе UDP - нет.

а как насчет 1 пакета? Если он прибудет, могу ли я быть уверен, что это полный пакет, а не кусок?

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

Дэвид Шварц
источник
10
Возможно, стоит уточнить последний бит для начинающих читателей: вы увидите полные данные для рассматриваемой дейтаграммы . Если какой-либо из разделенных пакетов будет потерян, датаграмма будет потеряна, и уровень UDP никогда не узнает об этом. Пока все пакеты в дейтаграмме получены, они будут собраны на уровне IP и затем переданы на уровень UDP. Это не исключает возможности пропустить «куски» в потоке данных. Не для того, чтобы быть педантом, но когда я изучал этот материал, я не чувствовал разницу между IP-фрагментом и потерей UDP до тех пор, пока 2-й или 3-й проход по учебнику.
Джастин ᚅᚔᚈᚄᚒᚔ
20

Вы не можете быть уверены, что они действительно физически прибывают сразу. Канальные уровни данных ниже TCP / UDP могут разделить ваш пакет, если они захотят. Особенно, если вы отправляете данные через Интернет или любые сети вне вашего контроля, это трудно предсказать.

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

Для UDP ОС также сделает некоторую абстракцию, поэтому приложение, которое получает данные, не должно знать, сколько пакетов было передано. Но отличие от TCP состоит в том, что нет никакой гарантии, что данные действительно поступят. Также возможно, что данные разбиваются на несколько пакетов, и некоторые из них поступают, а некоторые нет. Для принимающего приложения оно все равно выглядит как поток данных, независимо от того, завершено оно или нет.

переигровка
источник
Разве драйвер сетевой карты не заботится о сборке пакетов, а не ядра?
bluehallu
2
@Hallucynogenyc: Если что-то не изменилось, интернет-протокол предназначен для разделения пакетов размером более 576 байтов в любой точке их пути, но не ожидает, что кто-либо, кроме конечного получателя, рекомбинирует их. Я думаю, что идея заключается в том, что использование больших пакетов в большинстве случаев было попыткой уменьшить накладные расходы; после того, как пакет был разделен в какой-то момент в его пути, накладные расходы уже были понесены, поэтому рекомбинация до того, как конечный получатель не сможет помочь, может привести к повреждению, если его придется повторно разделить.
суперкат
Я считаю, что, хотя любой пакет, размер которого превышает 576 байтов, может быть разделен, пакеты, размер которых меньше этого размера, могут отсутствовать; встроенные системы, которые не могут работать с разделенными пакетами, должны избегать запроса чего-либо большего, чем это.
суперкат
1
@ mauro.stettler: я написал стек TCP на «голом железе» (написание кода для непосредственного общения с несколькими чипами сетевого интерфейса). Для оборудования, которое взаимодействует с каналом с ограничением в 576 байтов, просто разбить более длинные пакеты. Повторная сборка пакетов намного сложнее, тем более что можно получить куски множества разных пакетов до того, как любой из них будет получен полностью.
суперкат
Есть некоторая надежда, что он не будет разделен для крошечных полезных нагрузок (около 10 или 20 байтов должно быть в порядке), поскольку для каждого скачка IP-пакетов на ipv4 требуется «гарантированный максимальный размер» : не менее 68 байтов (включая Заголовки IP, не считая заголовков нижнего уровня). см. 1-ю таблицу в en.wikipedia.org/wiki/Maximum_transmission_unit . Отличается от 576 байтов минимального размера, требуемого от HOSTS (т. Е. Начала или конца передачи, не всех промежуточных переходов). И осторожно: полезная нагрузка еще ниже (поскольку заголовки каждого слоя занимают некоторое пространство).
Оливье Дюлак
14

Примеры. Блоки смежных символов соответствуют вызовам send ():

TCP:

Send: AA BBBB CCC DDDDDD E         Recv: A ABB B BCC CDDD DDDE

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

UDP:

Send: AA BBBB CCC DDDDDD E         Recv: CCC AA E

Данные не обязательно находятся в том же порядке и совсем не обязательно принимаются, но сообщения сохраняются во всей их полноте.

Джим Кот
источник
5

Например: если я отправлю 20 байтов по протоколу TCP, могу ли я быть на 100% уверен, что получу ровно 20 байтов одновременно, а не 10 байтов, тогда еще 10 байтов или около того?

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

Changaco
источник
1

Существует некоторая уверенность в том, что если вы отправите 20 байтов в самом начале потока TCP, он не будет получен как две 10-байтовые части. Это связано с тем, что стек TCP не будет отправлять такие маленькие сегменты: минимальный размер MTU. Однако, если отправка находится где-нибудь в середине потока, все ставки выключены. Возможно, ваш стек протоколов занимает 10 байт данных для заполнения сегмента и отправки его, а затем следующие десять байтов переходят в другой сегмент.

Ваш стек протоколов разбивает данные на куски и помещает их в очередь. Размеры чанка основаны на MTU пути. Если вы выполняете операцию отправки, и данные в очереди все еще находятся в ожидании, стек протоколов обычно просматривает сегмент, находящийся в хвосте очереди, и видит, есть ли место в этом сегменте для добавления дополнительных данных. Комната может быть размером всего в один байт, поэтому даже двухбайтовую передачу можно разбить на две части.

С другой стороны, сегментация данных означает, что возможны частичные чтения. Операция приема может потенциально активироваться и получать данные, когда приходит всего один сегмент. В широко реализованном API сокетов для вызова приема может запрашиваться 20 байтов, но он может возвращаться с 10. Конечно, на нем может быть построен уровень буферизации, который будет блокироваться до получения 20 байтов или до разрыва соединения. В мире POSIX этот API может быть стандартным потоком ввода-вывода: вы можете fdopenиспользовать дескриптор сокета для получения FILE *потока и использовать freadего для заполнения буфера таким образом, чтобы полный запрос удовлетворял столько readвызовов, сколько требуется ,

UDP датаграммы формируют данные. Каждый отправляющий вызов генерирует дейтаграмму (но о пробке см. Ниже). Другая сторона получает полную дейтаграмму (и в API сокетов она должна указывать достаточно большой буфер для ее хранения, иначе дейтаграмма будет обрезана). Большие дейтаграммы фрагментируются IP-фрагментацией и прозрачно собираются для приложений. Если какой-либо фрагмент отсутствует, вся дейтаграмма теряется; в этой ситуации невозможно прочитать частичные данные.

Существуют расширения интерфейса, позволяющие нескольким операциям указывать одну дейтаграмму. В Linux сокет может быть «закупорен» (запрещен для отправки). Пока они укупорены, записанные данные собираются в единый блок. Затем, когда сокет «откупорен», можно отправить одну дейтаграмму.

Kaz
источник
это неверно: если послать пакет с полезной нагрузкой 10 или 20 байт, это сгенерирует 1 пакет, и (как я уже говорил выше), если используется ipv4, он должен, даже при добавлении всех заголовков других уровней протокола, соответствовать в пределах 68 байтов, таким образом гарантируя, что он проходит все прыжки в 1 пакете. Стек Tcp не будет (как намекнуто в вашем первом абзаце) «ждать, пока mtu заполнится (т.е. добавить несколько пакетов, чтобы получить пакет правильного размера)», чтобы отправить пакет! ... Такое поведение может сломать многие вещи ( даже если эти «фрагменты» были отправлены от и к одной и той же паре хостов)
Оливье Дюлак
@OlivierDulac: Это неверно. TCP обычно генерирует пакеты по мере необходимости, пытаясь оптимизировать использование сети, поэтому 20 байтов могут оказаться в двух разных пакетах, как объяснено в Kaz. Это можно контролировать с помощью опции сокета TCP_NODELAY , которая отключает алгоритм Nagles, который отправляет байты в пакеты, если вашему приложению требуется более быстрая передача данных по TCP. Кроме того, 68 байтов ни в коем случае не являются стандартом де-факто для длины пакета: 1500 байтов - более обычное значение по умолчанию (это действительно различается в разных сетях).
jjmontes