Несколько sendto() с использованием сокета UDP

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

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

У меня следующий алгоритм:

  1. Разделить сообщение на небольшие фрагменты данных (около 1500 байт)
  2. Переберите список фрагментов данных и для каждого отправьте его с помощью sendto()

Однако, когда я отправляю много блоков данных, получатель получает только первые 6 сообщений. Иногда он пропускает шестой и получает седьмой. По-разному.

В любом случае, sendto() всегда указывает на успех. Это всегда происходит, когда я тестирую свое программное обеспечение через петлевой интерфейс (127.0.0.1), но никогда не через свою локальную сеть.

Если я добавлю что-то вроде std::cout << "test" << std::endl; между sendto(), то будут получены все кадры.

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

Какой подход здесь будет правильным?

  • Реализация какого-либо механизма подтверждения (так же, как TCP) кажется излишним.
  • Добавление некоторого произвольного времени ожидания между sendto() некрасиво и, вероятно, снизит производительность.
  • Увеличение (если возможно) внутреннего буфера UDP получателя? Я даже не знаю, возможно ли это.
  • Что-то другое ?

Мне очень нужны ваши советы здесь.

Спасибо большое.

Дополнительная информация по запросу

Причина, по которой я должен использовать UDP, заключается в том, что у меня есть несколько ограничений:

  1. TCP не работает хорошо с обходом NAT (по крайней мере, без специальной настройки)
  2. Некоторые сообщения могут быть потеряны. Некоторые другие не могут.
  3. Порядок доставки сообщений не имеет значения.

person ereOn    schedule 18.04.2010    source источник
comment
TCP не разрабатывался с намерением сделать его тяжеловесным. Он был разработан, чтобы удовлетворить четкое требование: надежная и упорядоченная доставка пакетов. Если вам нужны такие же гарантии, то нет ярлыка, TCP-light, который вы можете реализовать вместо TCP. Что бы вы ни делали, по сути, это будет TCP, только более глючный.   -  person Marcelo Cantos    schedule 18.04.2010
comment
@ Марсело Да, я полностью согласен. Именно поэтому я хочу избежать повторной реализации колеса.   -  person ereOn    schedule 18.04.2010
comment
У вас особенно плохой NAT-маршрутизатор? Отправка периодических тактов решает большинство проблем с NAT.   -  person nos    schedule 18.04.2010
comment
Я не понимаю, что TCP плохо работает с обходом NAT. UDP намного сложнее заставить работать через NAT.   -  person Marcelo Cantos    schedule 18.04.2010
comment
@nos Мой проект должен работать почти во всех возможных конфигурациях, а не только в моей. У меня уже есть система сердцебиения, чтобы поддерживать соединение UDP.   -  person ereOn    schedule 18.04.2010
comment
@Marcelo Cantos: Моя программа использует архитектуру одноранговой сети. Хостам часто приходится использовать пробивание отверстий UDP для установления успешной связи. Сделать это с помощью TCP совершенно невозможно.   -  person ereOn    schedule 18.04.2010
comment
Я вижу сейчас; вы пытаетесь внедрить STUN. Это еще более сложная проблема, чем надежный UDP. Начните здесь для получения информации и некоторых библиотек, которые вы можете использовать.   -  person Marcelo Cantos    schedule 18.04.2010


Ответы (7)


Внедрение механизма подтверждения кажется именно тем, что вам нужно сделать. Таким образом, вы можете гарантировать, что не более N пакетов находятся «в полете» одновременно, и вы можете повторно передавать пакеты, которые слишком долго оставались неподтвержденными.

person caf    schedule 18.04.2010
comment
Спасибо за Ваш ответ. Есть ли у вас какие-либо советы относительно алгоритма, который я должен использовать? Я предполагаю, что отправка одного подтверждения для каждого полученного фрагмента неоптимальна. - person ereOn; 18.04.2010
comment
Как уже говорили другие, это действительно будет зависеть от специфики ваших требований. Отправка одного подтверждения для каждого полученного пакета, вероятно, будет уместна (подтверждения будут небольшими), по крайней мере, в качестве первого шага. - person caf; 18.04.2010
comment
Требование 1 подтверждения для каждого пакета перед отправкой следующего может быть очень неоптимальным. Скажем, RTT составляет 25 мс. Затем вы можете отправлять только 1 сообщение каждые 25 мс, если ваши пакеты имеют максимальный размер 1500, это 60 КБ/сек - не очень быстро, если вам нужна скорость. - person nos; 18.04.2010
comment
В итоге я реализовал механизм подтверждения для каждого пакета. Работает нормально, без потери скорости. (Мне не нужно соблюдать порядок доставки, поэтому я могу отправлять сообщения/подтверждения асинхронно, избегая тем самым проблем, предложенных @nos). - person ereOn; 18.04.2010

Если вы теряете пакеты через петлевой интерфейс после отправки только 6 или 7 пакетов, возможно, ваш буфер приема слишком мал. Вы можете увеличить размер с помощью setsockopt, используя параметр SO_RCVBUF. Однако, если вы отправляете 1500 байт, то, если это действительно проблема, это означает, что буфер приема составляет всего около 9 КБ (или, что более вероятно, 8 КБ, однако это кажется довольно маленьким по умолчанию). Я считаю, что в Windows буфер приема по умолчанию составляет 16 КБ.

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

person Mark Wilkins    schedule 18.04.2010
comment
+1, потому что вы единственный, кто говорил об опции SO_RCVBUF. - person ereOn; 18.04.2010

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

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

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

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

person user64123    schedule 17.01.2012

Вы должны реализовать подтверждение и повторную передачу. Требовать, например. acks для каждых N пакетов и хранить N пакетов в буфере повторной передачи.

(возможно, вы можете получить некоторые идеи от rudp, или реализовать Il через UDP )

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

person nos    schedule 18.04.2010

TCP существует для решения именно таких проблем. Почему TCP не вариант? Вам придется решать все те же проблемы и в конечном итоге получить то же решение, только без преимуществ десятилетий исследований, разработок и отладки стека TCP.

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

Не зная вашей специфики, невозможно ответить на ваш вопрос простым «Делай это, и все будет хорошо», за исключением, конечно, «Делайте TCP, и все будет хорошо».

person Marcelo Cantos    schedule 18.04.2010
comment
Спасибо за ваш ответ. Я добавил дополнительную информацию в конце вопроса. - person ereOn; 18.04.2010

UDP передает дейтаграммы и ненадежен.

TCP передает потоки данных и является надежным.

То, что вы хотите, похоже, основано на дейтаграммах, но надежно. Поэтому вам нужно что-то построить на UDP или TCP, чтобы дать вам это. Я уверен, что существуют полные спецификации протокола, основанные на UDP, которые обеспечивают именно это. Вам остается только найти и реализовать их.

person ndim    schedule 18.04.2010

В любом случае, sendto() всегда указывает на успех. Это всегда происходит, когда я тестирую свое программное обеспечение через петлевой интерфейс (127.0.0.1), но никогда не через свою локальную сеть.

Если я добавлю что-то вроде std::cout ‹‹ test ‹‹ std::endl; между sendto() затем принимается каждый кадр.

Похоже, буфер вашего приемника слишком мал.

несколько советов:

  1. Увеличьте буфер приемника. setSockopt SO_RCVBUF
  2. Пусть приложение решит, следует ли повторно передать потерянный пакет.
  3. Убедитесь, что полезная нагрузка соответствует MTU, что означает полезная нагрузка + HEADER ‹= 1500, чтобы избежать фрагментации ip.
person Chen Fei    schedule 21.10.2014