Для теста накидал вот такое вот приложение. Задача — иметь возможность менять порт во время работы приложения.
#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;
}
Периодически вылазию пакеты с нулевой длинной. Если раскомментировать миллисекундные задержки, все норм.
Вроде защитил мъютексом, не должен порт меняться, пока выполняется асинхронный обработчик.
От куда тогда нулевые пакеты?
Здравствуйте, 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);
Т.е. система мне говорит, что в сокете что-то есть, я вызываю процедуру для чтения в первом потоке, а в это время контекст переключается, и второй поток "меняет" сокет, разумеется делая его "пустым". Вот нули и выскакивают.
Спасибо.