web-dev-qa-db-ja.com

2つのUDPサーバーが同じポートでリッスンできるようにしますか?

UDPブロードキャストを介してデータを送信するクライアントがあります。 (127.0.0.255:12345としましょう)

ここで、複数のサーバーでこのデータをリッスンしたいと思います。ローカルマシンでこれを行うには、リスニング用にポート12345を共有する必要があります。

私の質問は、それが可能かどうか、不利な点があるかどうか、そしてこのアプローチに問題があるかどうかです。

残念ながら、多くのオーバーヘッドをもたらす代替案が1つあります。
ある種の登録プロセスを実装します。起動時に、各サーバーはクライアントにそのポートを通知します。次に、クライアントは各ポートにメッセージを送信します(データを複数回送信する必要があるため、何らかのハンドシェイクを実装する必要があります...)
もっと良い選択肢を知っていますか?

それが重要な場合:
Boost :: AsioでC++を使用しています。ソフトウェアはポータブルである必要があります(主にLinuxとWindows)。

16
MOnsDaR

この回答は、SO_REUSEPORTが私が達成しようとしている効果をもたらすと述べているドキュメントをリンクしたcdhowieの回答を参照しています。

このオプションがどのように実装されているかを調査し、主にBoost :: AsioとLinuxに焦点を当てました。

Boost :: Asioは、OSがBSDまたはMacOSXと等しい場合にのみこのオプションを設定します。そのためのコードはファイルboost/asio/detail/reactive_socket_service.hppに含まれています(Boostバージョン1.40、新しいバージョンでは、コードは他のファイルに移動されています)。
なぜAsioがLinuxやWindowsのようなプラットフォームに対してこのオプションを定義しないのか疑問に思いました。

これがLinuxに実装されていないことを議論しているいくつかの参考文献があります: https://web.archive.org/web/20120315052906/http://kerneltrap.org/mailarchive/linux-netdev/2008/8/7/2851754
http://kerneltrap.org/mailarchive/linux-kernel/2010/6/23/4586155

この機能をカーネルに追加するパッチもあります: https://web-beta.archive.org/web/20110807043058/http://kerneltrap.org/mailarchive/linux-netdev/2010/ 4/19/627499

このオプションがWindowsに存在するかどうかはわかりませんが、Linuxでも実行されるソフトウェアの属性としてportableを定義することにより、SO_REUSEPORTはOS固有であり、私の質問に対する移植可能なソリューションがないことを意味します。 。

私がリンクした議論の1つでは、UDPがマスターリスナーを実装することをお勧めします。マスターリスナーは、受信データを複数のスレーブリスナーに提供します。

ポータブルソフトウェアでSO_REUSEPORTを使用しようとすると失敗する理由を指摘しているので、この回答を承認済みとしてマークします(ただし、自分の回答を受け入れることで少し気分が悪くなります)。

6
MOnsDaR

SO_REUSEPORTオプションを使用して、bothプロセスのソケットをバインドする必要があります。最初のプロセスでこのオプションを指定しない場合、2番目のプロセスでのバインドは失敗します。同様に、最初にこのオプションを指定し、2番目には指定しない場合、2番目のバインドは失敗します。このオプションは、要求( "別のプロセスによって既にバインドされている場合でもこのポートにバインドしたい")アクセス許可( "otherプロセスもこのポートにバインドする可能性があります」)。

詳細については、 このドキュメント のセクション4.12を参照してください。

24
cdhowie

いくつかの情報源は、WindowsでSO_REUSEADDRを使用する必要があると説明しています。しかし、ソケットをバインドしてなし UDPメッセージを受信できることについては言及されていません。以下のコードは、ソケットをローカルのlisten_endpointにバインドします。これがないと、UDPメッセージを受信できますが、デフォルトでは、ポートの排他的所有権があります。

ただし、ソケット(またはTCPを使用する場合はアクセプター)でreuse_address(true)を設定し、後でソケットをバインドすると、複数のアプリケーション、または独自のアプリケーションの複数のインスタンスで再度実行できるようになり、全員がすべてを受け取ります。メッセージ。

// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
    listen_address, multicast_port);

// == important part ==
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// == important part ==

boost::array<char, 2000> recvBuffer;
socket_.async_receive_from(boost::asio::buffer(recvBuffer), m_remote_endpoint,
        boost::bind(&SocketReader::ReceiveUDPMessage, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
4
Jan Wilmans