Фрагментация TCP/UDP и Ethernet MTU

Я читал различные сайты и учебные пособия в Интернете, но я все еще в замешательстве. Если сообщение больше, чем IP MTU, то send() возвращает отправленный байт. Что происходит с остальной частью сообщения? Должен ли я снова позвонить send() и попытаться отправить остальную часть сообщения? Или это то, о чем IP-уровень должен заботиться автоматически?


person Fantastic Fourier    schedule 09.03.2010    source источник


Ответы (2)


Если вы используете TCP, то представленный вам интерфейс представляет собой поток байтов. Вам не нужно беспокоиться о том, как поток байтов попадает с одного конца соединения на другой. Вы можете игнорировать MTU IP-уровня. На самом деле вы можете полностью игнорировать уровень IP.

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

Единственное, что нужно помнить, это то, что с TCP вы имеете дело с потоком, и это означает, что один send() может привести к поступлению данных в несколько вызовов recv(), а несколько вызовов send() могут привести к поступлению данных в один вызов recv(). У вас нет контроля над этим. Вы имеете дело с потоком байтов, и каждый вызов recv() может вернуть любое количество байтов от 1 до текущего числа (с учетом адекватных буферов, переданных вызову recv()).

Так как комментаторы просили об этом ;)

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

В любом случае, если send() возвращает меньше, чем количество байтов, которое вы ему предоставили, вам следует повторно отправить оставшиеся данные в какой-то момент. Часто send() будет блокировать и ждать, пока он не сможет отправить все данные, и если вы установили сокет в неблокирующий режим, то вы, вероятно, НЕ хотите немедленно повторять попытку отправки, если он не может отправить все, поскольку вы, вероятно, закончите в тугой петле...

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

person Len Holgate    schedule 09.03.2010
comment
+1, но для завершения вы также должны разобраться с тем, что представляет наибольший интерес для вопроса: если значение, возвращаемое send, меньше, чем запрошенные байты для отправки (очень маловероятный случай, так как стек попытается справиться с этим) вы несете ответственность за то, чтобы попытаться отправить остальные данные позже, если потребуется. - person David Rodríguez - dribeas; 09.03.2010
comment
Это действительно маловероятно? Потому что я, кажется, получаю меньшее значение, возвращаемое send(), чем запрошенные байты для отправки все время, когда запрошенный байт превышает MTU. - person Fantastic Fourier; 09.03.2010
comment
Юникс в c. Но вы подробно ответили на мой вопрос. Большое спасибо!! - person Fantastic Fourier; 09.03.2010

Если пакет слишком велик для передачи по сети, отправляется подсказка о фрагментации ICMP, сигнализирующая отправителю уменьшить размер пакета и повторить попытку.

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

WRT UDP вы все еще можете ожидать, что стек будет фрагментирован для вас, но практически, учитывая вариант использования UDP, он не идеален. В зависимости от вашего приложения вы, вероятно, увидите лучшую производительность, явно понимая MTU пути.

... в вопросе send() некоторые стеки ведут себя по-разному, но обработка WRT вашего кода должна быть одинаковой. Допустим, у вас есть 100 байтов для отправки... send() возвращает 10 отправленных байтов. Вам нужно продолжать вызывать send с оставшимися 90 байтами, пока все не будет отправлено по проводу для отправки всего сообщения.

Использование блокирующих сокетов на платформе Windows send() обычно возвращается после того, как все отправлено. На других платформах. Linux и др. Вам нужно будет чаще отправлять данные, чтобы отправлять данные.

person Einstein    schedule 09.03.2010