Несколько sendto () с использованием сокета UDP - PullRequest
4 голосов
/ 18 апреля 2010

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответы [ 7 ]

2 голосов
/ 17 января 2012

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

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

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

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

2 голосов
/ 18 апреля 2010

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

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

1 голос
/ 18 апреля 2010

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

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

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

1 голос
/ 18 апреля 2010

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

0 голосов
/ 21 октября 2014

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

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

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

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

  1. Увеличить буфер приемника. setsockopt SO_RCVBUF
  2. Пусть приложение решит, следует ли повторно передавать потерянный пакет.
  3. Убедитесь, что полезная нагрузка соответствует MTU, что означает полезную нагрузку + HEADER <= 1500, чтобы избежать фрагментации ip. </li>
0 голосов
/ 18 апреля 2010

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

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

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

0 голосов
/ 18 апреля 2010

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

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

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

...