Как сделать отключение на UDP-сокете, используя boost::asio

Объединяя информацию из надбавки и руководства для функции соединения, я понимаю, что функция asio:: udp:: socket:: connect служит для установки адреса, по которому дейтаграммы отправляются по умолчанию, и единственного адреса, с которого принимаются дейтаграммы.

Это хорошо работает, но в руководстве также говорится, что я должен быть в состоянии устранить ассоциацию (разъединение), что является проблемой, с которой я сталкиваюсь. Т.е. я бы хотел, чтобы сокет снова начал получать пакеты с любого адреса.

Далее в руководстве написано:

Connectionless sockets may dissolve the association by connecting to an
address with the sa_family member of sockaddr set to AF_UNSPEC.

Я пробовал этот код:

asio::udp::endpoint unspecified_endpoint;
assert(unspecified_endpoint.address().is_unspecified()); // OK
socket.connect(unspecified_endpoint);

Но это не помогло.


РЕДАКТИРОВАТЬ: создать тестовый пример, взяв примеры клиентов и серверов со страницы boost:: asio. В коде клиента я изменил номер исходящего порта со случайного на 5192:

udp::socket s(io_service, udp::endpoint(udp::v4(), 5192));

И я изменил функцию сервера в коде сервера:

void server(boost::asio::io_service& io_service, short port)
{
  udp::socket sock(io_service, udp::endpoint(udp::v4(), port));

#define CASE 3

#if CASE == 1
  sock.connect(udp::endpoint());
#elif CASE == 2
  sock.connect(udp::endpoint(ip::address::from_string("127.0.0.1"), 5193));
  sock.connect(udp::endpoint());
#elif CASE == 3
  sock.connect(udp::endpoint(ip::address::from_string("192.168.1.3"), 5192));
  sock.connect(udp::endpoint());
#endif

  for (;;)
  {
    char data[max_length];
    udp::endpoint sender_endpoint;
    size_t length = sock.receive_from(
    boost::asio::buffer(data, max_length), sender_endpoint);
    sock.send_to(boost::asio::buffer(data, length), sender_endpoint);
  }
}

Случаи 1 и 2, кажется, работают, но в случае 3 клиент не получит ответ. Полный источник можно найти здесь.

2 ответа

Решение

Как уже упоминалось в комментариях, призыв к sock.connect(udp::endpoint()); переводит на connect(6, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 что не то же самое, что connect с AF_UNSPEC и не полностью отключает сокет UDP (кажется, только для сброса номера порта равноправного узла).

Я не думаю, что есть способ достичь того, чего вы хотите, просто с помощью boost asio, но вы можете просто использовать собственный вызов POSIX для отключения сокета, что-то вроде

struct sockaddr unspec_addr = { AF_UNSPEC };
::connect(sock.native_handle(), &unspec_addr, sizeof(unspec_addr));

Примечания по отладке: запустите программу с strace а затем использовать netstat -nu перечислить UDP-сокеты.

Чтобы сохранить доступ к библиотечным функциям, используйте тот факт, что udp::endpointэто просто обертка. Построить sockaddrс определенным значением и привести его к требуемому типу.

      sock.connect(reinterpret_cast<const udp::endpoint &>(static_cast<const sockaddr &>(sockaddr{ AF_UNSPEC })));
Другие вопросы по тегам