4

質問が以前に回答されている場合は事前にお詫び申し上げますが、検索しても役に立たないものは見つかりませんでした。質問のタイトルが示すように、サーバーからメッセージをリッスンしている一連のクライアントにパッケージをブロードキャストしようとしています。

クライアントは、1 秒間に受信したメッセージの数をカウントします。

サーバー側は次のようになります。

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 を使用しています。

4

1 に答える 1

11

これが同じマシンで機能することに少し驚いています。ポート 1666 をリッスンしているクライアントが、ポート 8888 のブロードキャスト アドレスに送信されたデータを受信するとは予想していませんでした。

bind()ローカルエンドポイント (ローカル アドレスとポートで構成される) をソケットに割り当てます。ソケットがエンドポイントにバインドされると、バインドされたアドレスとポートに送信されたデータのみをソケットが受信するように指定されます。多くの場合、 にバインドすることをお勧めしaddress_v4::any()ます。これは、使用可能なすべてのインターフェイスをリッスンに使用するためです。複数のインターフェイス (複数の NIC カードの可能性あり) を持つシステムの場合、特定のインターフェイス アドレスにバインドすると、ソケットは指定されたインターフェイスから受信したデータのみをリッスンします[1]。したがって、resolve()アプリケーションが特定のネットワーク インターフェイスにバインドする必要があり、IP を直接 (127.0.0.1) または名前 (localhost) を提供して解決することをサポートしたい場合に、アドレスを取得しなければならないことがあります。

ソケットにバインドする場合、エンドポイントはアドレスとポートの両方で構成されることに注意してください。これは、同じマシンで動作するという私の驚きの源です。サーバーがブロードキャスト: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 は、アダプターが受信したブロードキャスト データグラムを、特定のインターフェイスにバインドされたソケットに渡します。

于 2013-06-07T17:55:50.257 に答える