Peter Jankuliak
Peter Jankuliak

Reputation: 3619

How to do disconnect on UDP socket using boost::asio

Combining the information from boost docs and manual for connect function I take it that the asio::udp::socket::connect function serves to set the address to which datagrams are sent by default, and the only address from which datagrams are received.

This works nicely but the manual also states that I should be able to dissolve the association (disconnect), which is what I'm having problem doing. I.e. I would like the socket to start receiving packets from any address again.

Further in the manual is written:

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

I have tried this code:

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

But that did not help.


EDIT: Created a test case by taking the client and server examples from boost::asio page. In client code, I've changed the outgoing port number from random to 5192:

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

And I changed the function server in the server code:

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);
  }
}

Cases 1 and 2 seem to work, but in case 3 the client wont receive a reply. The full source can be found here.

Upvotes: 2

Views: 1916

Answers (2)

Doliman100
Doliman100

Reputation: 121

To keep access to library functions, use the fact that udp::endpoint is just a wrapper around sockaddr. Construct sockaddr with a specific value and cast it to the required type.

sock.connect(reinterpret_cast<const udp::endpoint &>(static_cast<const sockaddr &>(sockaddr{ AF_UNSPEC })));

Upvotes: 0

cmeerw
cmeerw

Reputation: 7356

As mentioned in the comments, the call to sock.connect(udp::endpoint()); translates to connect(6, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 which isn't the same as a connect with AF_UNSPEC and doesn't completely disconnect the UDP socket (it appears to only reset the peer port number).

I don't think there is a way to achieve what you want purely with boost asio, but you could just use a native POSIX call to disconnect the socket, something like

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

Debugging notes: run the program with strace and then use netstat -nu to list the UDP sockets.

Upvotes: 2

Related Questions