Модераторы: feodorv
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [Boost::asio::ip::udp] не полное чтение дейтаграмм 
V
    Опции темы
cupper
Дата 1.3.2012, 17:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Проверяю как поведет себя система если будет получена дейтаграмма с большими чем ожидалось данными.

Код

socket_.receive_from(boost::asio::buffer(raw_buffer.get_buffer(), raw_buffer.get_size()), sender_endpoint);

Особенность в том что в Win кидается исключение с текстом
Код

A message sent on a datagram socket was larger than the internal message buffer or some other network limit, 
or the buffer used to receive a datagram into was smaller than the datagram itself

ну а в буферое ровно столько сколько я запросил.

В Linux исключение не бросается, данных опять ровно столько сколько запросил. Почему исключение не бросается ? Или почему бросается...

Вот сама функция из boost 1.44
Код

/// Receive a datagram with the endpoint of the sender.
  /**
   * This function is used to receive a datagram. The function call will block
   * until data has been received successfully or an error occurs.
   *
   * @param buffers One or more buffers into which the data will be received.
   *
   * @param sender_endpoint An endpoint object that receives the endpoint of
   * the remote sender of the datagram.
   *
   * @returns The number of bytes received.
   *
   * @throws boost::system::system_error Thrown on failure.
   *
   * @par Example
   * To receive into a single data buffer use the @ref buffer function as
   * follows:
   * @code
   * boost::asio::ip::udp::endpoint sender_endpoint;
   * socket.receive_from(
   *     boost::asio::buffer(data, size), sender_endpoint);
   * @endcode
   * See the @ref buffer documentation for information on receiving into
   * multiple buffers in one go, and how to use it with arrays, boost::array or
   * std::vector.
   */
  template <typename MutableBufferSequence>
  std::size_t receive_from(const MutableBufferSequence& buffers,
      endpoint_type& sender_endpoint)
  {
    boost::system::error_code ec;
    std::size_t s = this->service.receive_from(
        this->implementation, buffers, sender_endpoint, 0, ec);
    boost::asio::detail::throw_error(ec);
    return s;
  }


Добавлено @ 17:54
Продолжу, в windows части юзается функция WSARecvFrom которая судя по всему возвращает ошибку
Код

WSAEMSGSIZE
The message was too large for the specified buffer and (for unreliable protocols only) any trailing portion of the message that did not fit into the 
buffer has been discarded.


Под линукс, все сводится к операции функции recvmsg... осталось понять почему не происходит ошибки и что произойдет если попробовать читать дальше. Дочитается часть недочитанной дейтаграммы (и будет у меня epic fail) или же все таки первая дейтаграмма отбросится, и будет ждатся вторая. Как в случае в win.

Это сообщение отредактировал(а) cupper - 1.3.2012, 17:58
PM MAIL   Вверх
cupper
Дата 1.3.2012, 18:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



из описание функций 
recv
int recvfrom
int recvmsg
Код

Все три функции возвращают длину сообщения при успешном завершении. Если сообщение слишком длинное и не поместилось в предоставленный буфер,
 лишние байты могут быть отброшены, в зависимости от типа сокета, на котором принимаются сообщения (см. socket(2)).


так... уже нащупывается...

И все... и где искать описание поведения ((

Это сообщение отредактировал(а) cupper - 1.3.2012, 18:31
PM MAIL   Вверх
boostcoder
Дата 1.3.2012, 20:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 13
Всего: 110



Цитата(cupper @  1.3.2012,  17:44 Найти цитируемый пост)
Почему исключение не бросается ? Или почему бросается...

походу, так реализовали в asio...

Цитата(cupper @  1.3.2012,  17:44 Найти цитируемый пост)
recvmsg... осталось понять почему не происходит ошибки

я не нашел никакой информации о том, что ошибка вообще будет установлена.

Цитата(cupper @  1.3.2012,  17:44 Найти цитируемый пост)
что произойдет если попробовать читать дальше. Дочитается часть недочитанной дейтаграммы (и будет у меня epic fail) или же все таки первая дейтаграмма отбросится, и будет ждатся вторая.

проверь.

PM WWW   Вверх
cupper
Дата 1.3.2012, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Цитата(boostcoder @ 1.3.2012,  20:15)
Цитата(cupper @  1.3.2012,  17:44 Найти цитируемый пост)
что произойдет если попробовать читать дальше. Дочитается часть недочитанной дейтаграммы (и будет у меня epic fail) или же все таки первая дейтаграмма отбросится, и будет ждатся вторая.

проверь.

Придется. Полистал "Unix Сетевое программирование", книженция блин большая, по оглавлению и главан с этими функциями ничего не нашел,  по идее там должно быть это описано.

Но что уж точно так это то что нужно юзать версию которая никогда не бросает исключение

Код

template<
    typename MutableBufferSequence>
std::size_t receive_from(
    const MutableBufferSequence & buffers,
    endpoint_type & sender_endpoint,
    socket_base::message_flags flags,
    boost::system::error_code & ec);

PM MAIL   Вверх
cupper
Дата 2.3.2012, 13:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



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

#include <iostream>
#include <string>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

namespace
{
    const size_t SERV_PORT = 9654;
    
    void err(const char* err)
    {
        std::cerr << "Err: " << err << std::endl;
        std::cerr << strerror(errno) << std::endl;
    }
}

int Socket(struct sockaddr_in* addr, const char* ip_addr = NULL)
{
    int sockfd;
    
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        err("create socket");
        return -1;
    }
    
    bzero(addr, sizeof(*addr));
    addr->sin_family = AF_INET;
    addr->sin_port = htons(SERV_PORT);
    
    if(ip_addr == NULL)
        addr->sin_addr.s_addr = htonl(INADDR_ANY);
    else
    {
        if(inet_aton(ip_addr, &(addr->sin_addr)) == 0)
        {
            err("do inet_aton");
            close(sockfd);
            return -1;
        }
    }
    
    if(ip_addr == NULL)
    {
        if(bind(sockfd, (sockaddr *)addr, sizeof(*addr)) < 0)
        {
            err("cannot bind");
            close(sockfd);
            return -1;
        }
    }
    
    return sockfd;
}

int main(int argc, char* argv[])
{    
    struct sockaddr_in servaddr, cliaddr;
    
    int serv_sock, cli_sock;
    
    std::cout << "Create server socket" << std::endl;
    if((serv_sock = Socket(&servaddr)) < 0)
        return 1;
    
    std::cout << "Create client socket" << std::endl;
    if((cli_sock = Socket(&cliaddr, "127.0.0.1")) < 0)
        return 1;
    
    std::cout << "Socket was created\n";
        
    const char msg[] = "This is a test message";
    sendto(cli_sock, msg, sizeof(msg), 0, (sockaddr *)&cliaddr, sizeof(cliaddr));
    
    char recvmsg[sizeof(msg) / 2];
    int bytesrecv;
    errno = 0;
    if ((bytesrecv = recvfrom(serv_sock, recvmsg, sizeof(recvmsg), 0, (sockaddr*)NULL, NULL)) < 0)
    {
        std::cout << "EEEERRROR" << std::endl;
    }
    
    std::cerr << strerror(errno) << std::endl;
    std::cout << "bytesrecv = " << bytesrecv << std::endl;
    std::cout << "recvmsg = " << std::string(recvmsg, bytesrecv) << std::endl;
    
    if ((bytesrecv = recvfrom(serv_sock, recvmsg, sizeof(recvmsg), 0, (sockaddr*)NULL, NULL)) < 0)
    {
        std::cout << "EEEERRROR" << std::endl;
    }
    
    std::cout << "bytesrecv = " << bytesrecv << std::endl;
    std::cout << "recvmsg = " << std::string(recvmsg, bytesrecv) << std::endl;
    
    close(serv_sock);
    close(cli_sock);
    
}


выхлоп
Код

Create server socket
Create client socket
Socket was created
Success
bytesrecv = 11
recvmsg = This is a t
... весим ждем данных


Это сообщение отредактировал(а) cupper - 2.3.2012, 13:20
PM MAIL   Вверх
boostcoder
Дата 2.3.2012, 13:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 13
Всего: 110



Цитата(cupper @  2.3.2012,  13:16 Найти цитируемый пост)
Повторное чтение недочитанной дейтаграммы как и ожидалось не происходит. 

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

Добавлено через 17 секунд
в смысле, в линуксе.

Добавлено через 8 минут и 34 секунды
хотя есть возможность узнать сколько данных готовы для чтения: http://www.boost.org/doc/libs/1_49_0/doc/h.../available.html
PM WWW   Вверх
cupper
Дата 2.3.2012, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Ошибка на самом деле есть. И получит ее можно при вызове
Код

int recvmsg(int s, struct msghdr *msg, int flags);  

Код

struct msghdr {
    void         * msg_name;     /* необязательный адрес */
    socklen_t    msg_namelen;    /* размер адреса */
    struct iovec * msg_iov;      /* массив для scatter/gather */
    size_t       msg_iovlen;     /* кол-во элементов в msg_iov */
    void         * msg_control;  /* вспомогательные данные, см. ниже */
    socklen_t    msg_controllen; /* длина буфера вспомогательных данных */
    int          msg_flags;      /* флаги принятого сообщения */
};

Код

Поле msg_flags в msghdr устанавливается при возврате из recvmsg(2). Оно может содержать несколько флагов:
...
MSG_TRUNC
означает, что хвостовая часть датаграммы была отброшена, потому что датаграмма была больше, чем предоставленный буфер.


таким образом поменяв в коде получение сообщения на
Код

if ((bytesrecv = recvmsg(serv_sock, &msg, 0)) < 0)
    {
        std::cout << "EEEERRROR" << std::endl;
    }
    
    std::cerr << "Errno: "<< strerror(errno) << std::endl;

    
    if(msg.msg_flags == MSG_TRUNC)
    {
        std::cerr << "The receive buffer less than was received\n";
    }


Мы не получаем ошибку ни в Errno, ни в recvmsg, но в msg.msg_flags она таки фигурирует. Хреного что в бусте это не учтено.
PM MAIL   Вверх
boostcoder
Дата 2.3.2012, 14:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 13
Всего: 110



Цитата(cupper @  2.3.2012,  13:46 Найти цитируемый пост)
Хреного что в бусте это не учтено.

напишу автору.
PM WWW   Вверх
cupper
Дата 2.3.2012, 14:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Цитата(boostcoder @ 2.3.2012,  13:25)
хотя есть возможность узнать сколько данных готовы для чтения: http://www.boost.org/doc/libs/1_49_0/doc/h.../available.html


это кстати хорошо. Втыкну у себя проверку что если буфер меньше чем доступно то будет исключение кидаться. Так хоть кроссплатформенное поведение будет smile 

Правда еще нужно понять а размер чего он возвращает: всей дейтаграммы, пользовательских данных, из одной или их всех дейтаграм... ну и прочее.
Там все сводится к ioctl
Код

size_t available(socket_type s, boost::system::error_code& ec)
{
  if (s == invalid_socket)
  {
    ec = boost::asio::error::bad_descriptor;
    return 0;
  }

  ioctl_arg_type value = 0;
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
  int result = error_wrapper(::ioctlsocket(s, FIONREAD, &value), ec);
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
  int result = error_wrapper(::ioctl(s, FIONREAD, &value), ec);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
  if (result == 0)
    ec = boost::system::error_code();
#if defined(ENOTTY)
  if (ec.value() == ENOTTY)
    ec = boost::asio::error::not_socket;
#endif // defined(ENOTTY)

  return ec ? static_cast<size_t>(0) : static_cast<size_t>(value);
}


Цитата

напишу автору. 

На что он скорее всего ответит
Цитата(boostcoder @ 2.3.2012,  13:25)

есть возможность узнать сколько данных готовы для чтения

smile

А еще это может так только в 1.44, Нужно еще на новых проверять. 

Это сообщение отредактировал(а) cupper - 2.3.2012, 14:11
PM MAIL   Вверх
boostcoder
Дата 2.3.2012, 14:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 13
Всего: 110



Цитата(cupper @  2.3.2012,  14:07 Найти цитируемый пост)
На что он скорее всего ответит

тут на лицо несоответствие поведения на win/lin платформах. а такого в здравом уме никто не допустит. по всей видимости, упустил.

Добавлено через 35 секунд
Цитата(cupper @  2.3.2012,  14:07 Найти цитируемый пост)
это может так только в 1.44

может.

PM WWW   Вверх
cupper
Дата 2.3.2012, 14:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



avialble не спасет при асинхронных операциях которые я юзаю для реализации "чтение с timeout" :(

Можно попробовать уже после операции чтения сравнить реальное полученное (а не возвращенное операцией чтения) число байт в дейтаграмме и сравнить его с размером предоставленного буфера... Хотя я не уверен что из буста я смогу вытащить реальный размер данных дейтаграммы.

Вообще да, это явно ошибка потому что функция WSARecvFrom (используема в win части) возвращает не число считанный байт а статус операции
Код

Return value
If no error occurs and the receive operation has completed immediately, WSARecvFrom returns zero.


А функция recvmsg (используемая в Linux) возвращает именно число считанных байт

А вот общий результат чтения и в первом и во втором случае получается из возвращенных значений. 


PS. avialble возвращает общее число пользовательских данных в сокете. Если в скокете несколько дейтаграм то сумму со всех. Это капец блин :(

PPS. Вы не поверите smile!!! но под виндовс так как я описал выше, а вот... тсссс..., под Linux сколько данных в одной дейтаграмме smile Даже если в сокете несколько.

 Я в печальке. Может я чего не так делаю... ну не может быть такой epic fail.

Это сообщение отредактировал(а) cupper - 2.3.2012, 16:31
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0778 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.