[Asio::udp] Не Кроссплатформенное Поведение

Итак, представьте ситуацию: у нас есть кроссплатформенный сервер, который должен получать данные по UDP. Вооружившись Asio, вы создаете сокет, создаете буфер для полученных данных и начинаете прослушивать.

  
  
   

udp::socket receiver(ios, udp::endpoint(udp::v4(), port)); char read_buf[buf_len]; udp::endpoint sender_point; receiver.receive_from(buffer(read_buf, sizeof(read_buf)), sender_point);

Что произойдет, если полученная дейтаграмма содержит больше данных, чем вы выделили для буфера? Воля неподобающее поведение на платформах WIN/LINUX. В Linux операция пройдет гладко, будет прочитано ровно столько, сколько было запрошено, остальное будет отброшено.

На Win вы также получите прочитанные данные, но дополнительно получите еще и исключение.

И вот почему это произойдет: Работа функции take_from сводится к выполнению следующего кода

#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) int result = error_wrapper(::WSARecvFrom(s, bufs, recv_buf_count, &bytes_transferred, &recv_flags, addr, &tmp_addrlen, 0, 0), ec); *addrlen = (std::size_t)tmp_addrlen; .

if (result != 0) return socket_error_retval; ec = boost::system::error_code(); return bytes_transferred; #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) msghdr msg = msghdr(); init_msghdr_msg_name(msg.msg_name, addr); msg.msg_namelen = *addrlen; msg.msg_iov = bufs; msg.msg_iovlen = count; int result = error_wrapper(::recvmsg(s, &msg, flags), ec); *addrlen = msg.msg_namelen; if (result >= 0) ec = boost::system::error_code(); return result;

Для получения данных из сокета используются функции WSARecvFrom для Win и Recvmsg для Linux. Результат функции (успешный/неуспешный) определяется следующей функцией.



template <typename ReturnType> inline ReturnType error_wrapper(ReturnType return_value, boost::system::error_code& ec) { #if defined(BOOST_WINDOWS) || defined(__CYGWIN__) ec = boost::system::error_code(WSAGetLastError(), boost::asio::error::get_system_category()); #else ec = boost::system::error_code(errno, boost::asio::error::get_system_category()); #endif return return_value; }

Из описания функции WSARecvFrom из этого следует, что он возвращает статус ошибки:

Если ошибок не произошло и операция получения завершилась немедленно, WSARecvFrom возвращает ноль.

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

Один из которых просто:
ВСАЕМСГСИЗЕ Сообщение было слишком большим для указанного буфера, и (только для ненадежных протоколов) любая конечная часть сообщения, которая не помещалась в буфер, была отброшена.

Семейные функции получение возвращает количество успешно прочитанных байтов или -1, если произошла ошибка, и статус в errno. Но среди ее ошибок нет ни одной, похожей на приведенную выше.

Выходит, это не баг Linux? Не совсем так.

В структуре msghdr, передаваемой в функцию Recvmsg (именно она используется в ASIO), код ошибки прописывается в поле msg_flags:

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

Этот флаг можно использовать только с пакетными протоколами.

Таким образом, одна и та же операция в Win расценивается как некорректная, а в Linux как правильная.

Что asio явно не учитывает. Одной из возникающих трудностей является невозможность под Win определить, что не вся датаграмма прочитана.

Возможным решением этой проблемы может быть использование функции доступный .

Но и здесь мы получаем еще более пугающее несоответствие.

Эта функция на платформах Win возвращает общее количество данных в сокете UDP (сумму данных всех датаграмм), а на платформах Linux — размер только первой (той, которая будет обработана при следующем чтении) датаграммы.

.

Вот полный пример всего вышеперечисленного: источник .

Выходной выигрыш: Исключение: take_from: сообщение, отправленное в сокет дейтаграммы, превышает внутренний буфер сообщений, или был превышен другой сетевой параметр.

Также возможно, что буфер для приема сообщения был меньше размера сообщения.

Какой-то тест Доступно: 36 Доступно: 18 Доступно: 0

вывод Linux: Какой-то тест Доступно: 18 Доступно: 18 Доступно: 0
Протестировано на версиях Boost 1.44 и 1.49. Спасибо за внимание.

Теги: #boost #c plus plus #C++

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.