Boost::asio потеря пакетов по TCP

349
09 августа 2017, 20:18

Имеется клиент-серверное приложение. Сервер передает сообщения клиенту, число отправленных сообщений фиксируется. На клиентской стороне происходит асинхронный прием при помощи библиотеки boost::asio. Пока что все действие происходит на localhost. Код приведен ниже.

void Client::on_read(Client *c, const boost::system::error_code &error, size_t received) {
    if ((boost::asio::error::eof == error) || (boost::asio::error::connection_reset == error)) {
        c->connectState(CONNECT_DISCONNECTED);
        string msg = "Соединение с  "+ c->verbal() + " потеряно, попытка повторного подключения...\n";
        cout <<msg;
        BOOST_LOG_SEV(lg, info) << msg;
        c->disconnect();
        c->connect();
    }
    else{
        c->processData(received);
        Sleep(50);
        c->sock()->async_read_some(buffer(c->buf(), MODES_CLIENT_BUF_SIZE), boost::bind(on_read, c, _1, _2));
    }
}

Проблема заключается в том, что часть пакетов не принимается клиентом. Причем число принятых пакетов больше, если поставить задержку перед очередным async_read_some(...). В чем может заключаться ошибка? Платформа Windows 10, MSVC 2013.

Answer 1

В TCP соединениях никто не гарантирует, что если с одной стороны отправлять пакетами по 1500 байт, то с другой будет прилетать и читаться такими же. И если на localhost ещё как то оно работает, то уже через обычный роутер часто нет. TCP гарантирует, что все байты придут и будет правильный порядок (либо соединение оборвется). Если же в TCP пакете потерялся байт/байты по середине или исказились данные, то это хоть и возможно теоретически (а больших датацентрах даже вылавливали подобное), но не нужно на это тратить силы (если только это не реализация tcp стека на какой то неведомой железяке с Китая).

Что же делать? При чтении проверять, пришел ли целый пакет данных (или даже несколько). Если да - парсить. А хвост оставлять. При последующих чтениях данные добавлять к этому хвосту и снова проверять.

Вполне возможно, что в Ваших пакетах нет явных признаков деления. Тогда очень плохо. Нужно либо выдумывать эвристику, либо модифицировать протокол. Два самых простых способа - это либо указывать размер в начале каждого пакета (тогда будет понятно, сколько читать, а сам размер имеет фиксированный размер), либо применять байт(ы)-разделитель (в текстовых протоколах, типа IRC это перевод строки). В бусте для этого есть read_until, которому нужно передать "предикат", который будет проверять, что пакет готов.

P.S. если Вы в Linux/Windows/Macos/FreeBSD столкнулись с тем, что по tcp теряются данные и приложение не все получает, то скорее всего

  • логическая ошибка в Вашем коде
  • гонка или что то подобное в Вашем коде
  • сторонний антивирь/фаервол
  • кривой драйвер
  • проблема в ОС (если дошли сюда, то это уже серьезно)
  • ошибка в протоколе tcp (джекпот)
READ ALSO
Gstreamer c++ ошибка &#39;G_IS_OBJECT (object)&#39; failed

Gstreamer c++ ошибка 'G_IS_OBJECT (object)' failed

Есть батник принимающий видео трансляцию по RTP средствами gstreamerПытаюсь написать программу, которая делал бы тоже самое, что бы не было нужды...

345
Контейнер &lt;set&gt; C++

Контейнер <set> C++

Возникла задача, в которой надо обращаться с двумя соседними элементами множества setТакой вопрос: как это сделать? Гуглил, нигде не нашел...

354
Не работает программа?

Не работает программа?

Есть данная программа которая работает с принтером, проблема заключается в том что при попытке прочитать с ком порта ничего не происходит,...

393
Перегрузка &ldquo;=&rdquo; для контейнера STL

Перегрузка “=” для контейнера STL

Нужно перегрузить оператор "=" (присваивания для следующих типов данных)

328