使用 boost::asio 获取 UDP 套接字远程地址

Posted

技术标签:

【中文标题】使用 boost::asio 获取 UDP 套接字远程地址【英文标题】:Get UDP Socket Remote Address using boost::asio 【发布时间】:2018-03-21 08:02:45 【问题描述】:

我想获取响应 UDP 广播的设备的 IP 地址。在下面的代码中,我执行 UDP 广播来查找设备。当设备响应时,我尝试使用 remote_endpoint() 获取远程端点。

但是应用程序崩溃并出现异常 boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: remote_endpoint: Socket is not connected

由于我对 C++ 和 boost::asio 非常陌生,有人可以解释一下我需要更改代码以获取远程端点的 IP 地址。

#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();

【问题讨论】:

它不在 UDP 的套接字中。它在接收到的数据报中。某处。 @EJP Asio 在这种情况下成功隐藏了实现细节 @sehe 尽管如此,它仍然不在 UDP 的套接字中,因为不一定连接 UDP 套接字。这就是我说“某处”的原因。 @EJP 我没有异议。 【参考方案1】:

我提到the other day:async_receive_from的endpoint&参数不是为了那个,我认为它是一个输出参数”

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

在这里您传递了对broadcastEndpoint 的引用,这可能不是您想要做的。 The documentation states那个参数是sender_endpoint

sender_endpoint

一个端点对象,接收数据报远程发送者的端点。 sender_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();

【讨论】:

以上是关于使用 boost::asio 获取 UDP 套接字远程地址的主要内容,如果未能解决你的问题,请参考以下文章

如何拆分接收到的 boost asio udp 套接字联合数据报

Boost.Asio UDP async_read_from 分段错误

使用 boost::asio 在同一主机上多播消息

使用 boost::asio 获取广播源 IP 地址

使用 boost::asio 通过 UDP 发送结构

Boost::Asio 点对点 udp 聊天