UDP-пакеты, увиденные Wireshark, выпало из (даже не дошло) WSARecvFrom

У меня есть довольно запутанная проблема.
Я использую большую библиотеку С++ для обработки некоторого проприетарного протокола через UDP в Windows XP/7. Он прослушивает один порт на протяжении всего запуска программы и ждет соединений от удаленных аналогов.

В большинстве случаев это работает хорошо. Однако из-за некоторых проблем, которые я испытал, я решил добавить простую отладочную печать сразу после вызова WSARecvFrom (функция win32, используемая в библиотеке, для возврата дейтаграмм из моего сокета, представляющего интерес, и сообщите, какой IP-адрес и порт, из которого они пришли).
Как ни странно, в некоторых случаях я обнаружил, что пакеты отбрасываются на уровне ОС (т.е. Я вижу их в Wireshark, у них есть правильный dst-порт, все контрольные суммы верны), но они никогда не появляются в отладочных отпечатках, которые я вложил в код).

Теперь я полностью из факта (люди часто упоминают слишком часто), что "UDP не гарантирует доставку" - но это не актуально, поскольку полученные пакеты машиной - я вижу их в Вирешарке.
Кроме того, я знаком с буферами ОС и возможностью пополнения, но здесь появляется странная часть...

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

  • Они маленькие. Многие из пакетов в протоколе большие, близкие к MTU, но все упавшие пакеты составляют менее 100 байт (брутто).
  • Они всегда являются одним из двух: SYN-эквивалент (т.е. первый пакет, отправленный нам партнером для инициирования обмена сообщениями) или эквивалент FIN (т.е. пакет, отправленный одноранговым узлом, когда он больше не интересуется разговором для нас).

Может ли одно из этих двух качеств повлиять на буферы ОС и вызвать случайное (или даже более интересное - выборочно) удаление пакетов?

Любой свет, пролитый на эту странную проблему, будет очень оценен.

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


EDIT (24/10/12):

Думаю, я, возможно, пропустил важную деталь. Кажется, что пакеты упали до прибытия, обменялись чем-то другим: они (и я начинаю верить, только они) отправляются на сервер "новыми" сверстниками, то есть сверстниками, которые он не пытался контакт до.

Например, если синэксклюзивный пакет поступает из однорангового узла *, который мы никогда раньше не видели, его не будет видеть WSARecvFrom. Однако, если мы отправили syn-эквивалентный пакет этому равноуровню сами (даже если он не ответил в то время), и теперь он отправляет нам эквивалент, мы это увидим.

(*) Я не уверен, является ли это одноранговым узлом, которого мы не видели (т.е. ip: порт), или просто порт, который мы не видели раньше.

Помогает ли это? Это какой-то вариант WinSock, о котором я никогда не слышал? (как я уже говорил выше, код не мой, поэтому он может использовать параметры сокетов, о которых я не знаю)

Еще раз спасибо!

Ответ 1

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

Если это проблема, вы могли бы по крайней мере уменьшить ее экземпляры, запросив большой буфер recv.

Вы можете проверить размер вашего recv-буфера сокета, используя

int recvBufSize;
int err = getsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (char*)&recvBufSize, sizeof(recvBufSize));

и вы можете установить его в большем размере, используя

int recvBufSize = /* usage specific size */;
int err = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (const char*)&recvBufSize, sizeof(recvBufSize));

Если вы все еще видите данные, полученные ОС, но не доставленные на ваш сокет-клиент, вы можете подумать о различных подходах к протоколированию. например.

  • Войдите в буфер ОЗУ и только время от времени печатайте его (при любом размере, который ваш профиль наиболее эффективен)
  • Журнал из потока с низким приоритетом, либо принимая, что требования к памяти для этого будут непредсказуемыми, либо добавит код для удаления данных из буфера журнала, когда он будет заполнен.

Ответ 2

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

Ответ 3

Отключите брандмауэр Windows.

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

Это мое самое логичное предположение, основанное на том, что вы сказали здесь в своем обновлении:

Кажется, что пакеты упали до прибытия, чтобы поделиться чем-то другим в общем: они (и я начинаю верить, только они) отправляются на сервер "новыми" сверстниками, то есть сверстниками, что он не пытался связаться раньше.

Ответ 4

Столкнулся с такой же проблемой и на Redhat-Linux. Это оказывается проблемой маршрутизации.

RCA выглядит следующим образом:

  1. Верно, что UDP может достичь конечного компьютера (замечено на Wireshark).
  2. Теперь маршрут к источнику не найден, поэтому ответа на Wireshark нет.
  3. В некоторых ОС вы можете увидеть пакет запроса на Wireshark, но ОС фактически не доставляет сокет пакетов (вы можете увидеть этот сокет в netstat-nap).
  4. В этом случае всегда проверяйте ping (ping <dest ip> -I<source ip>)