使用Boost.Asio广播问题 [英] Issue with broadcast using Boost.Asio

查看:221
本文介绍了使用Boost.Asio广播问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果问题先前已回答,我们会提前道歉,但我已搜寻并找到任何有助于我的事。如问题标题所示,我试图从服务器广播一个包到一组客户端侦听任何消息。



客户端将计算



服务器端的操作如下:

 

code> class Server
{
public:

服务器(boost :: asio :: io_service& io)
:socket(io,udp ::端点(udp :: v4(),8888)
,broadcastEndpoint(address_v4 :: broadcast(),8888)
,tickHandler(boost :: bind(& Server :: Tick,this,boost :: asio :: placeholder :: 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 :: 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>缓冲;

};



以下列方式运行:

  int main()
{
try
{
boost :: asio :: io_service io;
服务器服务器(io);
io.run();
}
catch(const std :: exception& e)
{
std :: cerr< e.what()< \\\
;
}

return 0;
}

这显然工作正常。现在来客户端...

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

int main(int argc,char * argv [])
{
if(argc!= 2)
{
std :: cerr<< 用法:< argv [0]<< < host> \\\
;
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()<< \\\
;

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

socket.bind(serverEndpoint);

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

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 = 0;
start = current;
}
}
}
catch(const std :: exception& e)
{
std :: cerr< e.what()< \\\
;
}

在同一台机器上运行服务器和客户端时,当我在不同于运行客户端的机器上运行服务器时。



首先,我觉得奇怪的是,我必须解决服务器地址。也许我不知道广播是如何工作的,但我认为服务器将发送消息使用其插座与广播选项打开,它会到达同一网络中的所有插座。



我读了你应该绑定客户端的套接字到 address_v4 :: any()地址。我这样做,它不工作(说一些关于已经使用地址/端口的套接字)。



提前感谢。



PS:我在Windows 8下。

解决方案

我有点惊讶,机。我不会期望客户端,听端口1666,接收数据发送到端口8888上的广播地址。



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



重要的是注意,当绑定到套接字时,端点由地址端口组成。这是我的惊喜的来源,它在同一台机器上工作。如果服务器正在写入广播:8888,绑定到端口1666的套接字不应该接收数据报。然而,这里是一个端点和网络的视觉效果:

  - 。 
.--------。|
.--------。地址:任何地址:任何
| | port:any / \ port:8888 | |||
|服务器| - (-----------> |地址:广播​​| ---------->) - | client ||'
| | \ port:8888 / | |'
'--------''------ $'

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



一个简单的示例如下。



服务器:

  #include< boost / asio.hpp> 

int main()
{
命名空间ip = boost :: asio :: ip;
boost :: asio :: io_service io_service;

//服务器绑定到任何地址和任何端口。
ip :: udp :: socket socket(io_service,
ip :: udp :: endpoint(ip :: udp :: v4(),0));
socket.set_option(boost :: asio :: socket_base :: broadcast(true));

//广播将转到端口8888。
ip :: udp :: endpoint broadcast_endpoint(ip :: address_v4 :: broadcast(),8888);

//广播数据。
boost :: array< char,4>缓冲;
socket.send_to(boost :: asio :: buffer(buffer),broadcast_endpoint);
}

客户:



< pre class =lang-cpp prettyprint-override> #include< iostream>

#include< boost / asio.hpp>

int main()
{
命名空间ip = boost :: asio :: ip;
boost :: asio :: io_service io_service;

//客户端绑定到端口8888上的任何地址(在服务器上发送
//广播数据的端口)。
ip :: udp :: socket socket(io_service,
ip :: udp :: endpoint(ip :: udp :: v4(),8888));

ip :: udp :: endpoint sender_endpoint;

//接收数据。
boost :: array< char,4>缓冲;
std :: size_t bytes_transferred =
socket.receive_from(boost :: asio :: buffer(buffer),sender_endpoint);

std :: cout<< got< bytes_transferred<< 字节。 << std :: endl;
}






与服务器位于同一位置,则可能是各种网络相关问题:








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


I apologize in advance if the question has been previously answered, but I've searched and found nothing that helps me. As indicated by the question's title, I'm trying to broadcast a package from a server to a set of clients listening for any message.

The client will count the number of messages it receives during one second.

The server side of things goes like this:

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;

};

It is initialized and run in the following way:

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;
}

This (apparently) works fine. Now comes the client...

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";
    }

This works when running both the server and client on the same machine, but doesn't when I run the server on a machine different from that of where I run the client.

First thing is, it seems odd to me that I have to resolve the server's address. Perhaps I don't know how broadcasting really works, but I thought the server would send a message using its socket with the broadcast option turned on, and it would arrive to all the sockets in the same network.

I read you should bind the client's socket to the address_v4::any() address. I did, it doesn't work (says something about a socket already using the address/port).

Thanks in advance.

PS: I'm under Windows 8.

解决方案

I am a bit surprised this works on the same machine. I would not have expected the client, listening to port 1666, to receive data being sent to the broadcast address on port 8888.

bind() assigns a local endpoint (composed of a local address and port) to the socket. When a socket binds to an endpoint, it specifies that the socket will only receive data sent to the bound address and port. It is often advised to bind to address_v4::any(), as this will use all available interfaces for listening. In the case of a system with multiple interfaces (possible multiple NIC cards), binding to a specific interface address will result in the socket only listening to data received from the specified interface[1]. Thus, one might find themselves obtaining an address through resolve() when the application wants to bind to a specific network interface and wants to support resolving it by providing the IP directly (127.0.0.1) or a name (localhost).

It is important to note that when binding to a socket, the endpoint is composed of both an address and port. This is the source of my surprise that it works on the same machine. If the server is writing to broadcast:8888, a socket bound to port 1666 should not receive the datagram. Nevertheless, here is a visual of the endpoints and networking:

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

The server binds to any address and any port, enables the broadcast option, and sends data to the remote endpoint (broadcast:8888). Clients bound to the any address on port 8888 should receive the data.

A simple example is as follows.

The server:

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

The client:

#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;
}


When the client is not co-located with the server, then it could be a variety of network related issues:

  • Verify connectivity between the server and client.
  • Verify firewall exceptions.
  • Verify broadcast support/exceptions on the routing device.
  • Use a network analyzer tool, such as Wireshark, to verify that the time to live field in the packets is high enough that it will not be discarded during routing.

1. On Linux, broadcast datagrams received by an adapter will not be passed to a socket bound to a specific interface, as the datagram's destination is set to the broadcast address. On the other hand, Windows will pass broadcast datagrams received by an adapter to sockets bound to a specific interface.

这篇关于使用Boost.Asio广播问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆