Grommit
Grommit

Reputation: 165

Boost ASIO and UDP Errors

I've got two test programs (A & B)that are nearly identical, that use the same boost asio UDP async code.

Here is the receive call:

_mSocket.async_receive_from(
            boost::asio::buffer(_mRecvBuffer), _mReceiveEndpoint,
            boost::bind(&UdpConnection::handle_receive, this,_mReceiveEndpoint,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
// _mReceiveEndpoint is known and good. the buffer is good too.

// here's the handler

void handle_receive(const udp::endpoint recvFromEP, const boost::system::error_code& error,std::size_t  bytesRecv/*bytes_transferred*/)
{
    boost::shared_ptr<std::string> message(new std::string(_mRecvBuffer.c_array(),bytesRecv));
    if (!error) 
      {
      doSomeThingGood();
      } 
else {
        cerr << "UDP Recv error : " << error << endl;
    }
}

So here's what happens, all on localhost.

If I start program 'A' first, then program 'B', 'A' gives a UDP Recv error : server:10061. Program 'A' continues to send just fine and 'B' receives just fine.

You can swap 'A' and 'B' in the above sentence and it is still true.

IF I attempt a reset of the bad read condition by calling mSocket.async_receive_from again, I get error 10054.

I've looked these errors up on the web..... not very helpful.

Anybody have any ideas as to what these mean, and how I can recover inside the program if this condition occurs? Is there a way to reset the socket?

Sanity check.... can both programs operate on loopback with only two ports? A send = 20000, A receive = 20001 B send = 20001, B receive = 20000

TL;DR It appears as though if I try to listen before I'm sending, I get an error & I can't recover from it. If I listen after sending, I'm fine.

-- EDIT - It appears that McAfee host intrusion prevention is doing something nasty to me.... If I debug in VS2010, I get stuck in their DLL.

Thanks

Upvotes: 0

Views: 1580

Answers (2)

Jan Wilmans
Jan Wilmans

Reputation: 813

Several sources explain that you should use SO_REUSEADDR on windows. But none mention that it is possible to receive UDP message with and without binding the socket. The code below binds the socket to a local listen_endpoint, that is essential, because without that you can and will still receive your UDP messages, but by default your will have exclusive ownership of the port.

However if you set reuse_address(true) on the socket (or on the acceptor when using TCP), and bind the socket afterwards, it will enable multiple applications, or multiple instances of your own application to do it again, and everyone will receive all messages.

// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
    listen_address, multicast_port);

// == important part ==
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// == important part ==

boost::array<char, 2000> recvBuffer;
socket_.async_receive_from(boost::asio::buffer(recvBuffer), m_remote_endpoint,
        boost::bind(&SocketReader::ReceiveUDPMessage, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)

source: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/multicast/receiver.cpp

Upvotes: 0

Grommit
Grommit

Reputation: 165

In my receive handler, I wasn't calling _mSocket.async_receive_from() again.... I just printed the error and exited.

Silly mistake, just posting here in case it helps anyone else.

Also for a similar problem with a different resolution: _mSocket.set_option(boost::asio::socket_base::reuse_address(true));

helps if you have multiple listeners.

Upvotes: 2

Related Questions