【问题标题】:Issue with broadcast using Boost.Asio使用 Boost.Asio 广播的问题
【发布时间】:2013-06-03 00:16:13
【问题描述】:

如果之前已经回答了这个问题,我提前道歉,但我已经搜索并没有发现任何对我有帮助的东西。如问题标题所示,我正在尝试将一个包从服务器广播到一组侦听任何消息的客户端。

客户端会统计它在一秒内收到的消息数。

服务器端是这样的:

class Server
{
public:

    Server(boost::asio::io_service& io)
        : socket(io, udp::endpoint(udp::v4(), 8888))
        , broadcastEndpoint(address_v4::broadcast(), 8888)
        , tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error))
        , timer(io, boost::posix_time::milliseconds(20))
    {
        socket.set_option(boost::asio::socket_base::reuse_address(true));
        socket.set_option(boost::asio::socket_base::broadcast(true));

        timer.async_wait(tickHandler);
    }

private:

    void Tick(const boost::system::error_code&)
    {
        socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint);

        timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20));
        timer.async_wait(tickHandler);
    }

private:

    udp::socket socket;
    udp::endpoint broadcastEndpoint;

    boost::function<void(const boost::system::error_code&)> tickHandler;
    boost::asio::deadline_timer timer;

    boost::array<char, 100> buffer;

};

它的初始化和运行方式如下:

int main()
{
    try
    {
        boost::asio::io_service io;
        Server server(io);
        io.run();
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << "\n";
    }

    return 0;
}

这(显然)工作正常。现在客户来了……

void HandleReceive(const boost::system::error_code&, std::size_t bytes)
{
    std::cout << "Got " << bytes << " bytes\n";
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << " <host>\n";
        return 1;
    }

    try
    {
        boost::asio::io_service io;

        udp::resolver resolver(io);
        udp::resolver::query query(udp::v4(), argv[1], "1666");

        udp::endpoint serverEndpoint = *resolver.resolve(query);
        //std::cout << serverEndpoint.address() << "\n";

        udp::socket socket(io);
        socket.open(udp::v4());

        socket.bind(serverEndpoint);

        udp::endpoint senderEndpoint;
        boost::array<char, 300> buffer;

        auto counter = 0;
        auto start = std::chrono::system_clock::now();

        while (true)
        {
            socket.receive_from(boost::asio::buffer(buffer), senderEndpoint);
            ++counter;

            auto current = std::chrono::system_clock::now();
            if (current - start >= std::chrono::seconds(1))
            {
                std::cout << counter << "\n";

                counter = 0;
                start = current;
            }
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << "\n";
    }

这在同一台机器上同时运行服务器和客户端时有效,但当我在与我运行客户端的机器不同的机器上运行服务器时不起作用。

首先,我必须解析服务器地址,这对我来说似乎很奇怪。也许我不知道广播究竟是如何工作的,但我认为服务器会在广播选项打开的情况下使用其套接字发送消息,并且它会到达同一网络中的所有套接字。

我读到您应该将客户端的套接字绑定到address_v4::any() 地址。我做到了,它不起作用(说明套接字已经在使用地址/端口)。

提前致谢。

PS:我在 Windows 8 下。

【问题讨论】:

    标签: c++ udp boost-asio broadcasting


    【解决方案1】:

    我有点惊讶这在同一台机器上工作。我没想到客户端在监听 1666 端口时会收到发送到 8888 端口广播地址的数据。

    bind() 为套接字分配一个 local 端点(由本地地址和端口组成)。当套接字绑定到端点时,它指定套接字将只接收发送到绑定地址和端口的数据。通常建议绑定到address_v4::any(),因为这将使用所有可用的接口进行监听。在具有多个接口的系统(可能是多个 NIC 卡)的情况下,绑定到特定接口地址将导致套接字仅侦听从指定接口接收的数据[1]。因此,当应用程序想要绑定到特定的网络接口并希望通过直接提供 IP (127.0.0.1) 或名称 (localhost) 来支持解析它时,人们可能会发现自己通过 resolve() 获取地址。

    需要注意的是,当绑定到套接字时,端点由地址端口组成。这是我惊讶的原因,它可以在同一台机器上运行。如果服务器正在写入广播:8888,则绑定到端口 1666 的套接字不应接收数据报。不过,这里是端点和网络的示意图:

                                                                   .--------.
                                                                  .--------.|
    .--------. address: any                         address: any .--------.||
    |        | port: any      /                  \    port: 8888 |        |||
    | server |-( ----------->| address: broadcast |----------> )-| client ||'
    |        |                \    port: 8888    /               |        |'
    '--------'                                                   '--------'
    

    服务器绑定到任何地址和任何端口,启用广播选项,并将数据发送到远程端点(广播:8888)。绑定到端口 8888 上的任何地址的客户端应该会收到数据。

    一个简单的例子如下。

    服务器:

    #include <boost/asio.hpp>
    
    int main()
    {
      namespace ip = boost::asio::ip;
      boost::asio::io_service io_service;
    
      // Server binds to any address and any port.
      ip::udp::socket socket(io_service,
                             ip::udp::endpoint(ip::udp::v4(), 0));
      socket.set_option(boost::asio::socket_base::broadcast(true));
    
      // Broadcast will go to port 8888.
      ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888);
    
      // Broadcast data.
      boost::array<char, 4> buffer;
      socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint);
    }
    

    客户:

    #include <iostream>
    
    #include <boost/asio.hpp>
    
    int main()
    {
      namespace ip = boost::asio::ip;
      boost::asio::io_service io_service;
    
      // Client binds to any address on port 8888 (the same port on which
      // broadcast data is sent from server).
      ip::udp::socket socket(io_service, 
                             ip::udp::endpoint(ip::udp::v4(), 8888 ));
    
      ip::udp::endpoint sender_endpoint;
    
      // Receive data.
      boost::array<char, 4> buffer;
      std::size_t bytes_transferred = 
        socket.receive_from(boost::asio::buffer(buffer), sender_endpoint);
    
      std::cout << "got " << bytes_transferred << " bytes." << std::endl;
    }
    

    当客户端与服务器不在同一位置时,可能是各种网络相关问题:

    • 验证服务器和客户端之间的连接。
    • 验证防火墙例外情况。
    • 验证路由设备上的广播支持/例外情况。
    • 使用网络分析工具(例如Wireshark)来验证数据包中的time to live 字段是否足够高,不会在路由期间被丢弃。

    1。在 Linux 上,适配器接收的广播数据报不会被传递到绑定到特定接口的套接字,因为数据报的目的地设置为广播地址。另一方面,Windows 会将适配器接收到的广播数据报传递给绑定到特定接口的套接字。

    【讨论】:

    • 你的回答很有启发性,我必须承认。我的错误是使用给定端口而不是端口 0 打开服务器的套接字。
    • 另外,我发布了错误的代码...这是同一程序的两个版本,我更改了其中一个的端口号。
    • 为什么你的代码使用 ip::udp::v4() 而不是 ip::address_v4::any()?两者都会导致完全相同的行为吗?
    猜你喜欢
    • 2018-12-23
    • 1970-01-01
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    相关资源
    最近更新 更多