Итак, представьте ситуацию: у нас есть кроссплатформенный сервер, который должен получать данные по UDP. Вооружившись Asio, вы создаете сокет, создаете буфер для полученных данных и начинаете прослушивать.
Что произойдет, если полученная дейтаграмма содержит больше данных, чем вы выделили для буфера? Воля неподобающее поведение на платформах WIN/LINUX. В Linux операция пройдет гладко, будет прочитано ровно столько, сколько было запрошено, остальное будет отброшено.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 вы также получите прочитанные данные, но дополнительно получите еще и исключение.
И вот почему это произойдет:
Работа функции 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++
-
Гилберт, Уолтер
19 Oct, 24 -
Рейтинг Блоггеров: Как И Почему
19 Oct, 24 -
Любимое Дело
19 Oct, 24