Boost asio socket multicast to a specific ethernet interface

南楼画角 提交于 2019-12-21 02:02:13

问题


I thought I had found the answer in the following example, but not quite.

boost::asio::ip::udp::socket socket(io_service); 
...
boost::asio::ip::address_v4 local_interface =
    boost::asio::ip::address_v4::from_string("1.2.3.4");
boost::asio::ip::multicast::outbound_interface option(local_interface);
socket.set_option(option);

How do I map eth0 to the appropriate outbound_interface option?


回答1:


The following code works fine on Windows and Mac OS X:

    const ip::udp::resolver::query queryIF( ip::udp::v4(),                   
                                        _description->getInterface(), "0" ); 
    const ip::udp::resolver::iterator interfaceIP =                          
        resolver.resolve( queryIF );                                         

    if( interfaceIP == end )                                                 
        return false;                                                        

    const ip::address ifAddr( ip::udp::endpoint( *interfaceIP ).address( )); 

    _read->set_option( ip::multicast::join_group( mcAddr.to_v4(),            
                                                  ifAddr.to_v4( )));         
    _write->set_option( ip::multicast::outbound_interface( ifAddr.to_v4()));

EDIT: I had some issues on Linux, but did not look into it yet. My guess is the socket option is ignored in favor of the routing table.




回答2:


I think the reason why your example and eile's example don't work is because you didn't set the SO_BINDTODEVICE socket option.

See this to know why it doesn't work: http://codingrelic.geekhold.com/2009/10/code-snippet-sobindtodevice.html

See this to know how to do it with boost::asio: http://permalink.gmane.org/gmane.comp.lib.boost.asio.user/2724




回答3:


/**************************************************************************//**
\brief      
\details    
*******************************************************************************/
class UDPClient : public BoostSocketClient
{
public:
    UDPClient ();
    virtual ~UDPClient();
    virtual ARLErrorCode_e open(int port_num, const char* network_type="ipv4", const char* ip_address="", uint32_t listen_interface=0);
    virtual ARLErrorCode_e send(u8* message, u32 size);
    virtual ARLErrorCode_e close();
    virtual bool isOpen();
    //virtual void onReceived(u8*, u32);

private:
    void startReceive();
    void handleReceive(const boost::system::error_code&, std::size_t);
    void handleSend(const boost::system::error_code& error, std::size_t bytes_transferred);

private:
    boost::asio::io_service send_ios_;
    std::unique_ptr<boost::asio::io_service::work> send_worker_;

    boost::asio::io_service receive_ios_;
    std::unique_ptr<boost::asio::io_service::work> receive_worker_;

    boost::thread send_thread_;
    boost::thread receive_thread_;
    boost::array<u8, 1024> _buffer;
    boost::asio::ip::udp::endpoint send_endpoint_;
    boost::asio::ip::udp::endpoint sender_endpoint_;
    boost::asio::ip::udp::endpoint listen_endpoint_;

    std::unique_ptr<boost::asio::ip::udp::socket> send_udp_socket_;
    std::unique_ptr<boost::asio::ip::udp::socket> receive_udp_socket_;

};


#include <ACCompLib/Include/Typedefs.h>
#include <ACCompLib/Include/ARLErrorCodes.h>
#include <NetLib/Platform/Boost/cpp/UDPClient.h>
#include "Ws2tcpip.h"
#include "Iphlpapi.h"


using namespace std;
using namespace boost;
using namespace asio;
using namespace ip;
using namespace NetLib;

/***************************************************************************//**
\brief          Constructor
\details        

*******************************************************************************/
UDPClient::UDPClient() 
{
    receive_worker_.reset(new boost::asio::io_service::work(receive_ios_));
}
/***************************************************************************//**
\brief          ctor
\details        
*******************************************************************************/
UDPClient::~UDPClient()
{
    try
    {
        receive_worker_.reset();
        //send_worker_.reset();
        if (send_thread_.joinable()) {
            send_thread_.join();
        }
        if (receive_thread_.joinable()) {
            receive_thread_.join();
        }
    }
    catch (std::exception& e)
    {
        std::string str = e.what();
    }
}

/***************************************************************************//**
\brief          
\details        
\note           
\param[in]      
*******************************************************************************/
ARLErrorCode_e UDPClient::open(int port_num, const char* network_type, const char* multicastAddress, uint32_t listen_interface)
{
    try
    {
        struct in_addr in;
        in.S_un.S_addr = listen_interface;
        char* address_listen = inet_ntoa(in);
        //const char* address_listen = "0.0.0.0";
        std::string address_mcast = multicastAddress;
        unsigned short address_port = port_num;

        boost::system::error_code ec;

        boost::asio::ip::address listen_addr = boost::asio::ip::address::from_string(address_listen, ec);
        boost::asio::ip::address mcast_addr = boost::asio::ip::address::from_string(address_mcast, ec);


        if (strcmp(network_type, "ipv4") == 0)
        {
            listen_endpoint_ = udp::endpoint(listen_addr, port_num);
            send_endpoint_ =  udp::endpoint(mcast_addr, port_num);
        }
        else if (strcmp(network_type, "ipv6") == 0)
        {
        }
        else
            return ES35_INVALID_SOCKET_CONNECTION;

        send_udp_socket_.reset(new boost::asio::ip::udp::socket(send_ios_));
        receive_udp_socket_.reset(new boost::asio::ip::udp::socket(receive_ios_));

        send_udp_socket_->open(boost::asio::ip::udp::v4());
        receive_udp_socket_->open(listen_endpoint_.protocol());
        send_udp_socket_->set_option(boost::asio::ip::udp::socket::reuse_address(true));
        receive_udp_socket_->set_option(boost::asio::ip::udp::socket::reuse_address(true));

        boost::asio::ip::address_v4 local_interface =
            boost::asio::ip::address_v4::from_string(address_listen);
        boost::asio::ip::multicast::outbound_interface option(local_interface);


        // Join the multicast group.
        receive_udp_socket_->set_option(
            boost::asio::ip::multicast::join_group(mcast_addr));

        send_udp_socket_->set_option(option);
        receive_udp_socket_->set_option(option);

        boost::asio::ip::multicast::hops hops_option(3);
        //send_udp_socket_->set_option(hops_option);
        receive_udp_socket_->set_option(hops_option);

        receive_udp_socket_->bind(listen_endpoint_);


        startReceive();
        receive_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &receive_ios_));
        send_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &send_ios_));


        return ES_NoError;
    }
    catch (std::exception& exp)
    {
        std::string str = exp.what();
        return ES35_INVALID_SOCKET_CONNECTION;
    }
}

/***************************************************************************//**
\brief          
\details        
*******************************************************************************/
ARLErrorCode_e UDPClient::close(void)
{
    try {
        boost::system::error_code ec;
        //udp_socket_->cancel();
        //udp_socket_.shutdown(socket_base::shutdown_both, ec);
        if (ec) 
        {
            return ES35_INVALID_SOCKET_CONNECTION;
        }
        if (send_udp_socket_->is_open()) 
        {
            send_udp_socket_->close();
        }
        if (receive_udp_socket_->is_open())
        {
            receive_udp_socket_->close();
        }

        receive_udp_socket_.reset();
        send_udp_socket_.reset();

    }
    catch (std::exception& e)
    {
        std::string str = e.what();
        return ES35_INVALID_SOCKET_CONNECTION;
    }

    return ES_NoError;
}

/***************************************************************************//**
\brief          
\details        
*******************************************************************************/
bool UDPClient::isOpen()
{
    return send_udp_socket_->is_open() && receive_udp_socket_->is_open();
}

/***************************************************************************//**
\brief      Send a message.     
\details    The message is sent asynchronously. 
\param      message
\param      message size
*******************************************************************************/
ARLErrorCode_e UDPClient::send(u8* message, u32 size)
{
    if (!isOpen()) {
        return ES35_INVALID_SOCKET_CONNECTION;
    }

    std::string send_to_address = send_endpoint_.address().to_string();



    send_udp_socket_->set_option(asio::ip::multicast::enable_loopback(false));

    send_udp_socket_->async_send_to(
        buffer(message, size),
        send_endpoint_,
        bind(
            &UDPClient::handleSend,
            this,
            asio::placeholders::error,
            asio::placeholders::bytes_transferred));


    return ES_NoError;
}

/***************************************************************************//**
\brief      Do nothing.         
\details    This function has the required signature to be used as an
            asynchronous send completion handler.
\param      not used
\param      not used
*******************************************************************************/
void UDPClient::handleSend(const system::error_code& error, size_t)
{
    if (error) 
    {
        BoostSocketClient::onError(ES35_UDP_SEND_ERROR, (u8*)error.message().c_str(), error.message().size());
    }
    else
    {
        send_udp_socket_->set_option(asio::ip::multicast::enable_loopback(true));
    }
}
/***************************************************************************//**
\brief  Start an asynchronous receiver.         
*******************************************************************************/
void NetLib::UDPClient::startReceive()
{

    receive_udp_socket_->async_receive_from(
        buffer(_buffer),
        sender_endpoint_,
        bind(
            &UDPClient::handleReceive,
            this,
            asio::placeholders::error,
            asio::placeholders::bytes_transferred));

    std::string sender_address = sender_endpoint_.address().to_string();
}

/***************************************************************************//**
\brief      Pass received data to the base class.   
\details    A new receiver is started.
\param      error code
\param      data size
*******************************************************************************/
void NetLib::UDPClient::handleReceive(const system::error_code& error, size_t size)
{
    if (!error || error == error::message_size)
    {
        BoostSocketClient::onReceived(_buffer.data(), size);

        startReceive();
    }
    else        
    {
        BoostSocketClient::onError(ES35_UDP_RECEIVE_ERROR, (u8*)error.message().c_str(), error.message().size());
    }
}


This code is not receiving response.please check



回答4:


http://permalink.gmane.org/gmane.comp.lib.boost.asio.user/2724 is invalid. The following code seems to be invalid:

boost::asio::ip::address_v4 local_interface = 
boost::asio::ip::address_v4::from_string(ip);
boost::asio::ip::multicast::outbound_interface option(local_interface);
sock.set_option(option);


来源:https://stackoverflow.com/questions/8593859/boost-asio-socket-multicast-to-a-specific-ethernet-interface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!