Написал приложение (клиент-сервер (C++/Linux(Ubuntu))), использую для асинхронности epoll и неблокирующие сокеты. Аналогичное приложение на Windows, только там WinSock2 и IOCP для асинхронности. В общем никогда не думал, что столкнусь с таким багом: мне сообщений приходит или больше, или меньше, чем я их отослал.
Теперь подробнее: есть модуль, который разбивает большие объемы данных на более мелкие (по 1000 байт) и отсылает их в цикле (сервер), этот же модуль (на клиенте) склеивает данные и предоставляет пользователю готовое сообщение (например при отсылке картинки). Модуль работает нормально, тестировал без сетевой части.
Как только начал передавать данные по сети — появились баги.
Долго не мог понять в чем дело, потом написал тест для сокетов: 2 UDP сокета в цикле отсылают данные друг-другу, а при обработке принятия данных на конкретный сокет — сделал ранее 2 переменные (int), обнулил их и делаю инкремент определенной переменной. Например: отсылаю 1000 раз на каждый сокет по 1024 байта. Тест провален. В результате переменные не равны 1000, а они меньше (в epoll) и больше (в IOCP) этого значения.
При чем: если поставить после каждой отсылки delay или cout/printf — все работает без сбоев!
В связи с этим вопрос: где может быть баг и какая максимальная частота отправки сообщений для UDP?
Спасибо!
08.05.12 12:45: Перенесено модератором из 'C/C++' — Кодт
08.05.12 13:41: Перенесено из 'Сети, сокеты, протоколы'
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>В связи с этим вопрос: где может быть баг и какая максимальная частота отправки сообщений для UDP?
It depends.
UDP не гарантирует доставку. И максимальную частоту вычислить не удастся — отбрасываться пакеты могут стеком TCP/IP и/или более нижних уровнях ОС передающей или принимающей стороны при переполнении буферов, на любом маршрутизаторе по пути следования.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>>В связи с этим вопрос: где может быть баг и какая максимальная частота отправки сообщений для UDP?
M>It depends. M>UDP не гарантирует доставку. И максимальную частоту вычислить не удастся — отбрасываться пакеты могут стеком TCP/IP и/или более нижних уровнях ОС передающей или принимающей стороны при переполнении буферов, на любом маршрутизаторе по пути следования.
На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
A_L>На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
Видеопоток будет лагать если канал забит и/или система не успевает его обрабатывать.
Можно проверять, что возвращают неблокирующие операции (они могут и не отправить данные), или проверять, сколько байт можно отправить, и, если недостаточно для отсылки новой орции данных, ухлдить в sleep, а не тупо после каждой отправки.
А вообще, читай еще раз мое предыдущее сообщение.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>>На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
M>Видеопоток будет лагать если канал забит и/или система не успевает его обрабатывать.
M>Можно проверять, что возвращают неблокирующие операции (они могут и не отправить данные), или проверять, сколько байт можно отправить, и, если недостаточно для отсылки новой орции данных, ухлдить в sleep, а не тупо после каждой отправки. M>А вообще, читай еще раз мое предыдущее сообщение.
До переполнения буфера еще очень далеко. Картинка, которую я пересылаю, меньше 60 кб, а объем буфера для UDP — 65507 байт (http://ru.wikipedia.org/wiki/UDP). За неблокирующими операциями следит eopll/IOCP, мне этого нет смысла делать...
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>До переполнения буфера еще очень далеко. Картинка, которую я пересылаю, меньше 60 кб, а объем буфера для UDP — 65507 байт (http://ru.wikipedia.org/wiki/UDP). За неблокирующими операциями следит eopll/IOCP, мне этого нет смысла делать...
Ну, во первых, кто вам гарантирует этот размер буфера? Вики оно для общего развития хорошо, чтобы первое впечатление получить, а так — с каждой системой надо отдельно разбираться. Далее — там описывают, что это макс размер данных при отсылке одного пакета. При отсылке нескольких пакетов полезный размер будет меньше. Далее, неясно, это буфер для одного приложения или общий для всех? Проверяй, сколько места есть в буфере, ну, или отсылай новую порцию тогда, когда предыдущая операция завершилась. Можно их несколько сделать, штуки три, например, и делать не sleep, а ожидание на ивентах этих операций.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>>До переполнения буфера еще очень далеко. Картинка, которую я пересылаю, меньше 60 кб, а объем буфера для UDP — 65507 байт (http://ru.wikipedia.org/wiki/UDP). За неблокирующими операциями следит eopll/IOCP, мне этого нет смысла делать...
M>Ну, во первых, кто вам гарантирует этот размер буфера? Вики оно для общего развития хорошо, чтобы первое впечатление получить, а так — с каждой системой надо отдельно разбираться. Далее — там описывают, что это макс размер данных при отсылке одного пакета. При отсылке нескольких пакетов полезный размер будет меньше. Далее, неясно, это буфер для одного приложения или общий для всех? Проверяй, сколько места есть в буфере, ну, или отсылай новую порцию тогда, когда предыдущая операция завершилась. Можно их несколько сделать, штуки три, например, и делать не sleep, а ожидание на ивентах этих операций.
Кстати, может и сработает, если так... Спасибо, буду пробовать)))
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
Так если свой протокол гарантирует доставку, может просто забить, и пофигу на потери? Глядишь, и скорость выше будет.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>Кстати, может и сработает, если так... Спасибо, буду пробовать)))
Для начала можно и с одним ивентом поработать, и посмотреть, что получиться, смысла особого в нескольких наверно нет.
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>В связи с этим вопрос: где может быть баг и какая максимальная частота отправки сообщений для UDP?
1. UDP не гарантирует (и не пытается гарантировать) доставку. Больше пакетов, чем пролезет в сеть, до получателя не дойдет. Отсылатель может об этом и не узнать
2. Пакеты могут пропадать не только локально, при попытке отправить их в сеть, но и по дороге, если промежуточный роутер не смог отправить пакет дальше
3. Больше пакетов, чем оправлено, принято быть не может (сейчас кто-нибудь сумничает и скажет про IP-фрагментацию. В UDP сокет пакеты попадают уже после дефрагментации, поэтому фрагментация не причем). Так что если вы получаете больше, чем отправляете, ищите ошибку в программе
4. В ваш UDP-сокет послать пакет может кто угодно. Поэтому надо уметь отличать свои пакеты от чужих. В TCP подкинуть пакет в чужое соединение гораздо сложнее
5. В венде по умолчанию очень маленький размер входной/выходной очереди для UDP. Даже при сравнительно небольшом траффике вы можете не успевать выгребать все пакеты из сокета. Более того, если у вашей программы приоритет по умолчанию, то в процессе отрисовки на экране чего-нибудь большого и красивого вас могут так надолго лишить процессора, что вы не успеете выгрести все даже с большими буферами. Поэтому приоритет того потока, который разгребает сеть, очень рекомендуется поднять.
6. Размер буферов можно поменять — см. setsockopt() на предмет SO_RCVBUF/SO_SNDBUF.
7. Пакеты могут приходить получателю не в том порядке, в котором они были отправлены, и если пересылать их не между соседними машинами, а через настоящий интернет, это и в самом деле иногда происходит
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
Торрент, при работе по UDP, реально сбрасывает скорость отправки, как только пакеты начинают теряться. Они это сделали специально, чтобы ихний траффик не мешался всему прочьему траффику только забыли, к сожалению, что сетевая инфраструктура не рассчитана на большие UDP-потоки
Здравствуйте, fddima, Вы писали:
Pzz>>только забыли, к сожалению, что сетевая инфраструктура не рассчитана на большие UDP-потоки F> Это почему?
Патамучта никто раньше не гнал большие UDP-потоки. И где на них можно было сэкономить, сэкономили. И теперь провайдеры плачутся, что у них все колом встает.
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>Всем привет!
A_L>Написал приложение (клиент-сервер (C++/Linux(Ubuntu))), использую для асинхронности epoll и неблокирующие сокеты. Аналогичное приложение на Windows, только там WinSock2 и IOCP для асинхронности. В общем никогда не думал, что столкнусь с таким багом: мне сообщений приходит или больше, или меньше, чем я их отослал.
A_L>Теперь подробнее: есть модуль, который разбивает большие объемы данных на более мелкие (по 1000 байт) и отсылает их в цикле (сервер), этот же модуль (на клиенте) склеивает данные и предоставляет пользователю готовое сообщение (например при отсылке картинки). Модуль работает нормально, тестировал без сетевой части. A_L>Как только начал передавать данные по сети — появились баги.
A_L>Долго не мог понять в чем дело, потом написал тест для сокетов: 2 UDP сокета в цикле отсылают данные друг-другу, а при обработке принятия данных на конкретный сокет — сделал ранее 2 переменные (int), обнулил их и делаю инкремент определенной переменной. Например: отсылаю 1000 раз на каждый сокет по 1024 байта. Тест провален. В результате переменные не равны 1000, а они меньше (в epoll) и больше (в IOCP) этого значения.
A_L>При чем: если поставить после каждой отсылки delay или cout/printf — все работает без сбоев!
A_L>В связи с этим вопрос: где может быть баг и какая максимальная частота отправки сообщений для UDP?
A_L>Спасибо!
Здравствуйте, Alex_Logvinenko, Вы писали:
A_L>На работает же как-то торрент? Тут ситуация такая же. Просто если после каждой операции отсылки в цикле ставить delay, то картинку размером 64кб принимает довольно долго, не говоря уже о видеопотоке. Доставку мне гарантирует мой протокол, как и порядок отправки/приема данных. И эта часть тоже работает, даже на сетевом уровне. Баг возникает только постоянной отсылке данных.
Не понял в итоге в чем баг. В Вашем протоколе, который должен гарантировать доставку и не гарантирует? Или в том, что UDP теряет пакеты. Если первое, то надо смотреть реализацию протокола, если второе — то это нормально. Поверх UDP, если это критично, придется городить код проверки на корректность, правильность последовательности и вообще, что сообщение дошло.
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Не понял в итоге в чем баг. В Вашем протоколе, который должен гарантировать доставку и не гарантирует? Или в том, что UDP теряет пакеты. Если первое, то надо смотреть реализацию протокола, если второе — то это нормально. Поверх UDP, если это критично, придется городить код проверки на корректность, правильность последовательности и вообще, что сообщение дошло.
К слову, на UDP даже контрольные суммы не обязательны (хотя на системах уровня хотя бы лаптопа сложно найти реализацию, где этого нет).
Pzz>3. Больше пакетов, чем оправлено, принято быть не может (сейчас кто-нибудь сумничает и скажет про IP-фрагментацию. В UDP сокет пакеты попадают уже после дефрагментации, поэтому фрагментация не причем). Так что если вы получаете больше, чем отправляете, ищите ошибку в программе
UDP uses a simple transmission model without implicit handshaking dialogues for providing reliability, ordering, or data integrity. Thus, UDP provides an unreliable service and datagrams may arrive out of order, appear duplicated, or go missing without notice.
То есть еще как может. Желающие найти потенциальные физиологические причины тому — могут покопаться в потрохах всех роутеров существующих в мире, но я бы просто добавил контроль на уровне приложенческого протокола — это проще.
Могу впрочем подкинуть один вариант: алгоритм детекта коллизий на физическом уровне сетевыми девайсами и перепосылка пакетов изза оных.
Как много веселых ребят, и все делают велосипед...
Pzz>>>только забыли, к сожалению, что сетевая инфраструктура не рассчитана на большие UDP-потоки F>> Это почему? Pzz>Патамучта никто раньше не гнал большие UDP-потоки. И где на них можно было сэкономить, сэкономили. И теперь провайдеры плачутся, что у них все колом встает.
А еще потому что UDP — connectionless, и соответственно — непонятно какую делать стратегию выделения памяти под хранение состояния UDP потоков (учитывая что память — она общая с TCP). Понятно что бывают крайности — типа параноидальных систем которые не шлют TCP FIN/RST и хакеров которые делают тоже самое, но все же, в статистически подавляющем количестве случаев, в момент когда TCP конекшен становится не нужен сторонам — промежуточные девайсы об этом узнают сразу, а UDP — будет жить (и занимать ресурсы, мешая другим), пока его не выпилит таймаут или low memory.
Как много веселых ребят, и все делают велосипед...
ononim wrote:
> А еще потому что UDP — connectionless, и соответственно — непонятно > какую делать стратегию выделения памяти под хранение состояния UDP > потоков (учитывая что память — она общая с TCP).
А можно пояснить где нужно выделять специально память? Ведь обычный
маршрутизатор должен работать на уровне IP пакетов.
>> А еще потому что UDP — connectionless, и соответственно — непонятно >> какую делать стратегию выделения памяти под хранение состояния UDP >> потоков (учитывая что память — она общая с TCP). _>А можно пояснить где нужно выделять специально память? Ведь обычный _>маршрутизатор должен работать на уровне IP пакетов.
Кроме обычных маршрутизаторов еще бывают IDS/IPS, NAT'ы иногда встречаются..
Как много веселых ребят, и все делают велосипед...