7

あるポートでクライアントからデータを受信するためにudpサーバーで同じソケットを使用し、後で要求の処理後にip :: ud :: socket::async_send_toを使用してクライアントに応答します。

受信はasync_receive_fromと非同期で行われます。ソケットは同じioServiceを使用します(結局のところ同じソケットです)同じudpソケットがクライアントAからデータグラムを(非同期で)受信し、場合によっては別のデータグラムをクライアントB(非同期で)に送信できるかどうかについては、ドキュメントに明確に記載されていません送信)同時に、これが問題につながる可能性があると思います。別のクライアントに応答しているときに別のソケットを同じサーバーポートにバインドできなかったため、応答に同じソケットを使用することになりました。

別のソケットを同じサーバーポートにバインドするにはどうすればよいですか?

編集します。2番目のudpソケットを同じUDPポートにバインドしようとしています:

socket(ioService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))

これを最初に行うとき(サーバーの「受信」ソケットのバインド)は問題ありませんが、バインド時にエラーを報告するように2回目に別のソケットを作成しようとすると(asioは例外をスローします)

4

1 に答える 1

16

あるリモートエンドポイントから同時に受信し、別のリモートエンドポイントに送信するUDPソケットを持つことができます。ただし、Boost.AsioスレッドとBoost.Asioのドキュメントによると、単一のオブジェクトで同時呼び出しを行うことは一般的に安全ではありません。

したがって、これは安全です。

thread_1 | thread_2
-------------------------------------- + --------------- ----------------------------
socket.async_receive_from(...); |
socket.async_send_to(...); |

そしてこれは安全です:

thread_1 | thread_2
-------------------------------------- + --------------- ----------------------------
socket.async_receive_from(...); |
                                      | socket.async_send_to(...);

しかし、これは安全ではないと指定されています:

thread_1 | thread_2
-------------------------------------- + --------------- ----------------------------
socket.async_receive_from(...); | socket.async_send_to(...);
                                      |

などの一部の関数は複合操作であり、追加のスレッドセーフ制限があることに注意しboost::asio::async_readください


次のいずれかに該当する場合、フローは暗黙的に同期されるため、追加の同期を行う必要はありません。

  • すべてのソケット呼び出しはハンドラー内で発生io_service::run()し、単一のスレッドからのみ呼び出されます。
  • async_receive_fromasync_send_to非同期操作の同じチェーン内でのみ呼び出されます。たとえば、ReadHandlerpassed toは、をasync_receive_from呼び出しasync_send_toWriteHandlerpassed toは、をasync_send_to呼び出しますasync_receive_from

    void read()
    {
      socket.async_receive_from( ..., handle_read );  --.
    }                                                   |
        .-----------------------------------------------'
        |      .----------------------------------------.
        V      V                                        |
    void handle_read( ... )                             |
    {                                                   |
      socket.async_send_to( ..., handle_write );  --.   |
    }                                               |   |
        .-------------------------------------------'   |
        |                                               |
        V                                               |
    void handle_write( ... )                            |
    {                                                   |
      socket.async_receive_from( ..., handle_read );  --'
    }
    

一方、ソケットを同時に呼び出す可能性のある複数のスレッドがある場合は、同期を行う必要があります。boost :: asio :: io_service :: strandを介して関数とハンドラーを呼び出すか、 Boost.Threadのmutexなどの他の同期メカニズムを使用して、同期を実行することを検討してください。


スレッドセーフに加えて、オブジェクトの存続期間の管理を考慮する必要があります。サーバーが複数の要求を同時に処理する必要がある場合は、各要求->プロセス->応答チェーンの所有権に注意bufferendpointください。のドキュメントasync_receive_fromによると、呼び出し元はバッファエンドポイントの両方の所有権を保持します。そのため、 boost::shared_ptrを使用してオブジェクトの存続期間を管理する方が簡単な場合があります。それ以外の場合、チェーンが十分に高速で並行チェーンが不要な場合は、管理が簡素化され、リクエストごとに同じバッファーエンドポイントを使用できるようになります。


最後に、このsocket_base::reuse_addressクラスを使用すると、ソケットをすでに使用されているアドレスにバインドできます。ただし、一般的に使用されているため、ここでは適切な解決策ではないと思います。

  • ポートが状態にある場合でも、TCPがプロセスを再起動し、同じポートをリッスンできるようにするためTIME_WAIT
  • UDPが複数のプロセスを同じポートにバインドできるようにし、各プロセスがマルチキャストを介して受信およびブロードキャストできるようにします。
于 2012-09-04T22:13:19.300 に答える