asio - откуда нулевые пакеты
От: dosik Россия www.dosik.ru
Дата: 17.03.17 12:19
Оценка:
Для теста накидал вот такое вот приложение. Задача — иметь возможность менять порт во время работы приложения.
#include "stdafx.h"
#include <iostream>
#define ASIO_STANDALONE
#include <asio.hpp>

class udp_service {
    std::thread worker;
    std::mutex sock_mutex;
    asio::io_context context;
    asio::ip::udp::socket sock;

    void set_async_task() {
        sock.async_receive(asio::null_buffers(), std::bind(&udp_service::handle_receive_from, this, std::placeholders::_1, std::placeholders::_2));
    }

    void handle_receive_from(std::error_code error, std::size_t bytes_recvd) {
        std::lock_guard<std::mutex> lock(sock_mutex);

        if (error != asio::error::operation_aborted)
            receive_and_process(error);

        set_async_task();
    };

    void receive_and_process(std::error_code error) {
        try {
            size_t size = sock.available();
            if (size == 0) {
                //От куда это берется????
                //error == 0
                return;
            }
            std::unique_ptr<char[]> buff(new char[size]);
            asio::ip::udp::endpoint ep;
            size = sock.receive_from(asio::buffer(buff.get(), size), ep, 0);
            std::cout << buff.get() << std::endl;
        }
        catch (std::exception& e) {
            std::cout << "Exception : " << e.what() << std::endl;
        }
    }

public:
    udp_service(unsigned short port) : sock(context) {
        set_port(port);
        set_async_task();
    };

    void set_port(unsigned short port) {
        std::lock_guard<std::mutex> lock(sock_mutex);
        if (sock.is_open())
            sock.close();

        asio::ip::udp::endpoint ep(asio::ip::udp::v4(), port);
        sock.open(ep.protocol());
        sock.set_option(asio::ip::udp::socket::reuse_address(true));
        sock.set_option(asio::socket_base::broadcast(true));
        sock.bind(ep);
    }

    void start() {
        if (worker.joinable())
            return;

        worker = std::thread([&]() { context.run(); });
    }

    void stop() {
        if (!worker.joinable())
            return;

        context.stop();
        worker.join();
    }
};

int main()
{
    setlocale(LC_ALL, "Russian");

    asio::io_service service;
    udp_service udp(5431);
    udp.start();

    asio::ip::udp::socket sock(service, asio::ip::udp::v4());
    asio::ip::udp::endpoint ep1(asio::ip::address::from_string("127.0.0.1"), 5432);
    asio::ip::udp::endpoint ep2(asio::ip::address::from_string("127.0.0.1"), 5433);
    char ch;
    do {
        udp.set_port(5432);
        sock.send_to(asio::buffer("hello 5432"), ep1);
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));
        udp.set_port(5433);
        sock.send_to(asio::buffer("hello 5433"), ep2);
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));

        std::cin >> ch;
    } while (ch != 'q');

    udp.stop();

    return 0;
}


Периодически вылазию пакеты с нулевой длинной. Если раскомментировать миллисекундные задержки, все норм.
Вроде защитил мъютексом, не должен порт меняться, пока выполняется асинхронный обработчик.
От куда тогда нулевые пакеты?
Re: asio - откуда нулевые пакеты
От: SkyDance Земля  
Дата: 20.03.17 18:30
Оценка:
D> if (sock.is_open())
D> sock.close();
<...>
D>От куда тогда нулевые пакеты?

Вот от этих двух выделенных мной строк. Закрываете сокет — через loopback прилетает event, который будет вашего receiver'а. С нулевым размером из recvfrom().
Re[2]: asio - откуда нулевые пакеты
От: dosik Россия www.dosik.ru
Дата: 20.03.17 18:44
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Вот от этих двух выделенных мной строк. Закрываете сокет — через loopback прилетает event, который будет вашего receiver'а. С нулевым размером из recvfrom().


На самом деле не так, уже разобрался, просто не стал отписываться.
Когда я закрываю сокет, event на самом деле пробуждается, но с ошибкой asio::error::operation_aborte, что в общем то и проверяется:
if (error != asio::error::operation_aborted)
            receive_and_process(error);


Нулевые пакеты появляются, если после чтения "нулевого буфера"
sock.async_receive(asio::null_buffers(), std::bind(&udp_service::handle_receive_from, this, std::placeholders::_1, std::placeholders::_2));

но до момента блокировки мьютекса в callback
void handle_receive_from(std::error_code error, std::size_t bytes_recvd) {
        std::lock_guard<std::mutex> lock(sock_mutex);
...

происходит переключение контекса и вызов с set_port
sock.send_to(asio::buffer("hello 5432"), ep1);
udp.set_port(5433);


Т.е. система мне говорит, что в сокете что-то есть, я вызываю процедуру для чтения в первом потоке, а в это время контекст переключается, и второй поток "меняет" сокет, разумеется делая его "пустым". Вот нули и выскакивают.

Спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.