2
votes

I would like to get the IP address of the device that responds to UDP broadcast. In the following code I perform UDP broadcast to find a device. When the device response I try to get the remote endpoint using remote_endpoint().

However the application crashes with an exception boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: remote_endpoint: Socket is not connected

As I am very new to C++ and boost::asio could someone explain what I need to change the code to be able to get the IP address of the remote endpoint.

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <iostream>

class udpFind {
public:
    udpFind(boost::asio::io_context& service, boost::asio::ip::udp::endpoint listen_endpoint, unsigned int port)
    : broadcastEndpoint_(boost::asio::ip::address_v4::broadcast(), port),
    timer(service),
    socket_(service, listen_endpoint)
    {
        socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
        socket_.set_option(boost::asio::socket_base::broadcast(true));

        find();
    }

    void find () {
        std::array<unsigned int, 2> data = {255, 255};
        for (auto it = std::begin(data); it != std::end(data); ++it)
            std::cout << *it;
        socket_.async_send_to(
                              boost::asio::buffer(data, 2), broadcastEndpoint_,
                              boost::bind(&udpFind::handle_send, this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
    }

    void handle_receive(const boost::system::error_code& error,
                        std::size_t bytes_transferred)
    {
        // Read has finished, so cancel the timer.
        timer.cancel();
        if (!error) {
            std::cout << "Received Data" << bytes_transferred << std::endl

            boost::asio::ip::udp::endpoint local_ep = socket_.local_endpoint();
            boost::asio::ip::udp::endpoint remote_ep = socket_.remote_endpoint();
        }
    }

    void handle_retry(const boost::system::error_code& error) {
        std::cout << "retrying"  << std::endl;
        find();
    }


    void handle_timeOut(const boost::system::error_code& error) {
        if (!error) {
            std::cout << "Timeout"  << std::endl;
            // Timer has expired cancel read operation
            socket_.cancel();
            timer.expires_from_now(boost::posix_time::milliseconds(10000));
            timer.async_wait(boost::bind(&udpFind::handle_retry,
                                         this, boost::asio::placeholders::error));
        }
    }


    void handle_send(const boost::system::error_code& error, std::size_t bytes_transferred)
    {
        std::cout << "Sent Data "  << bytes_transferred << std::endl;
        buffer_= {};
        socket_.async_receive_from(
                                   boost::asio::buffer(buffer_), broadcastEndpoint_,
                                   boost::bind(&udpFind::handle_receive, this,
                                               boost::asio::placeholders::error,
                                               boost::asio::placeholders::bytes_transferred));

        timer.expires_from_now(boost::posix_time::milliseconds(10000));
        timer.async_wait(boost::bind(&udpFind::handle_timeOut,
                                     this, boost::asio::placeholders::error));
    }

private:
    boost::asio::ip::udp::socket socket_;
    std::array<char, 128> buffer_;
    boost::asio::ip::udp::endpoint broadcastEndpoint_;
    boost::asio::deadline_timer timer;
};

int main()
{
    boost::asio::io_context service;
    boost::asio::ip::udp::endpoint listen_endpoint(boost::asio::ip::address::from_string("0.0.0.0"), 0);
    udpFind find(service, listen_endpoint, 9000);
    service.run();
}
1
It isn't in the socket in UDP. It is in the received datagram. Somewhere.user207421
@EJP Asio successfully hides the implementation specifics of that in this casesehe
@sehe Nevertheless it still isn't in the socket in UDP, because a UDP socket isn't necessarily connected. That's why I said 'somewhere'.user207421
@EJP I didn't dispute anything.sehe

1 Answers

1
votes

I mentioned the other day: "the endpoint& parameter to async_receive_from is not for that, I think it is an output parameter"

socket_.async_receive_from(
                           boost::asio::buffer(buffer_), broadcastEndpoint_,
                           boost::bind(&udpFind::handle_receive, this,
                                       boost::asio::placeholders::error,
                                       boost::asio::placeholders::bytes_transferred));

Here you pass a reference to broadcastEndpoint which is likely not what you wanted to do. The documentation states that parameter is sender_endpoint:

  • sender_endpoint

    An endpoint object that receives the endpoint of the remote sender of the datagram. Ownership of the sender_endpoint object is retained by the caller, which must guarantee that it is valid until the handler is called.

So, the solution is simply to add a variable to receive the remote endpoint:

Live On Coliru

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/bind.hpp>
#include <iostream>

class udpFind {
public:
    udpFind(boost::asio::io_context& service, boost::asio::ip::udp::endpoint listen_endpoint, unsigned int port)
    : broadcastEndpoint_(boost::asio::ip::address_v4::broadcast(), port),
      timer(service),
      socket_(service, listen_endpoint)
    {
        socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
        socket_.set_option(boost::asio::socket_base::broadcast(true));

        find();
    }

    void find () {
        std::array<unsigned int, 2> data = {{255, 255}};
        for (auto it = std::begin(data); it != std::end(data); ++it)
            std::cout << *it;

        socket_.async_send_to(
                              boost::asio::buffer(data, 2), broadcastEndpoint_,
                              boost::bind(&udpFind::handle_send, this,
                                          boost::asio::placeholders::error,
                                          boost::asio::placeholders::bytes_transferred));
    }

    void handle_receive(const boost::system::error_code& error,
                        std::size_t bytes_transferred)
    {
        // Read has finished, so cancel the timer.
        timer.cancel();
        if (!error) {
            std::cout << "Received Data" << bytes_transferred << " from " << senderEndpoint_ << std::endl;
        }
    }

    void handle_retry(const boost::system::error_code& error) {
        std::cout << "retrying"  << std::endl;
        find();
    }


    void handle_timeOut(const boost::system::error_code& error) {
        if (!error) {
            std::cout << "Timeout"  << std::endl;
            // Timer has expired cancel read operation
            socket_.cancel();
            timer.expires_from_now(boost::posix_time::milliseconds(10000));
            timer.async_wait(boost::bind(&udpFind::handle_retry,
                                         this, boost::asio::placeholders::error));
        }
    }


    void handle_send(const boost::system::error_code& error, std::size_t bytes_transferred)
    {
        std::cout << "Sent Data "  << bytes_transferred << " (" << error.message() << ")" << std::endl;
        buffer_= {};
        socket_.async_receive_from(
                                   boost::asio::buffer(buffer_), senderEndpoint_,
                                   boost::bind(&udpFind::handle_receive, this,
                                               boost::asio::placeholders::error,
                                               boost::asio::placeholders::bytes_transferred));

        timer.expires_from_now(boost::posix_time::milliseconds(10000));
        timer.async_wait(boost::bind(&udpFind::handle_timeOut,
                                     this, boost::asio::placeholders::error));
    }

private:
    boost::asio::ip::udp::endpoint broadcastEndpoint_;
    boost::asio::deadline_timer timer;
    boost::asio::ip::udp::socket socket_;
    boost::asio::ip::udp::endpoint senderEndpoint_;
    std::array<char, 128> buffer_;
};

int main()
{
    boost::asio::io_context service;
    boost::asio::ip::udp::endpoint listen_endpoint(boost::asio::ip::address::from_string("0.0.0.0"), 0);
    udpFind find(service, listen_endpoint, 9000);
    service.run();
}