28

私は、1 つのスレッドがクライアントからコマンドを受信する tcp サーバーとして機能するマルチスレッド アプリケーションに取り組んでいます。スレッドは、Boost ソケットとアクセプターを使用してクライアントが接続するのを待ち、クライアントからコマンドを受け取り、そのコマンドをアプリケーションの残りの部分に渡し、再び待ちます。コードは次のとおりです。

void ServerThreadFunc()
{
    using boost::asio::ip::tcp;
    boost::asio::io_service io_service;
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_no));

    for (;;)
    {
        //  listen for command connection
        tcp::socket socket(io_service);
        acceptor.accept(socket);

        //  connected; receive command
        boost::array<char,256> msg_buf;
        socket.receive(boost::asio::buffer(msg_buf));

        //  do something with received bytes here
    }
}

このスレッドは、ほとんどの時間を への呼び出しでブロックされていますacceptor.accept()。現時点では、スレッドはアプリケーションの終了時にのみ終了します。残念ながら、これにより main() が返された後にクラッシュが発生します-シングルトンが破棄された後にスレッドがアプリのロギングシングルトンにアクセスしようとするためだと思います。(私がここに来たときはそうでした、正直なところ。)

アプリケーションを終了するときに、このスレッドをきれいにシャットダウンするにはどうすればよいですか? 別のスレッドからソケットを閉じることで、raw ソケットでの accept() 呼び出しのブロックを中断できることを読みましたが、これは Boost ソケットでは機能しないようです。Boost 非同期 tcp エコー サーバーの例を使用して、サーバー ロジックを非同期 i/o に変換しようとしましたが、それは へのブロッキング呼び出しをacceptor::accept()へのブロッキング呼び出しと交換しているように見えるio_service::run()ので、同じ問題が残ります: ブロックされた割り込めない電話。何か案は?

4

5 に答える 5

30

つまり、次の 2 つのオプションがあります。

  • コードを非同期 ( acceptor::async_accept()and async_read) に変更し、 を介してイベント ループ内で実行し、 を介しio_service::run()てキャンセルしio_service::stop()ます。
  • シグナルなどの低レベルの仕組みでブロック呼び出しを強制的に中断させます。

最初のオプションをお勧めします。これは、移植性が高く、保守が容易である可能性が高いためです。理解しておくべき重要な概念は、io_service::run()保留中の作業がある限りブロックするということです。がio_service::stop()呼び出されると、ブロックされているすべてのスレッドio_service::run()をできるだけ早く返そうとします。同期操作がイベント ループ内で呼び出された場合でも、acceptor::accept()やなどの同期操作は中断されません。はノンブロッキング コールであるため、ブロックされたスレッドと同期するには、 などの別のメカニズムを使用する必要があるsocket::receive()ことに注意してください。io_service::stop()io_service::run()thread::join()

以下は、10 秒間実行され、ポート 8080 をリッスンする例です。

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>

void StartAccept( boost::asio::ip::tcp::acceptor& );

void ServerThreadFunc( boost::asio::io_service& io_service )
{
  using boost::asio::ip::tcp;
  tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), 8080 ) );

  // Add a job to start accepting connections.
  StartAccept( acceptor );

  // Process event loop.
  io_service.run();

  std::cout << "Server thread exiting." << std::endl;
}

void HandleAccept( const boost::system::error_code& error,
                   boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
                   boost::asio::ip::tcp::acceptor& acceptor )
{
  // If there was an error, then do not add any more jobs to the service.
  if ( error )
  {
    std::cout << "Error accepting connection: " << error.message() 
              << std::endl;
    return;
  }

  // Otherwise, the socket is good to use.
  std::cout << "Doing things with socket..." << std::endl;

  // Perform async operations on the socket.

  // Done using the socket, so start accepting another connection.  This
  // will add a job to the service, preventing io_service::run() from
  // returning.
  std::cout << "Done using socket, ready for another connection." 
            << std::endl;
  StartAccept( acceptor );
};

void StartAccept( boost::asio::ip::tcp::acceptor& acceptor )
{
  using boost::asio::ip::tcp;
  boost::shared_ptr< tcp::socket > socket(
                                new tcp::socket( acceptor.get_io_service() ) );

  // Add an accept call to the service.  This will prevent io_service::run()
  // from returning.
  std::cout << "Waiting on connection" << std::endl;
  acceptor.async_accept( *socket,
    boost::bind( HandleAccept,
      boost::asio::placeholders::error,
      socket,
      boost::ref( acceptor ) ) );
}

int main()
{
  using boost::asio::ip::tcp;

  // Create io service.
  boost::asio::io_service io_service;

  // Create server thread that will start accepting connections.
  boost::thread server_thread( ServerThreadFunc, boost::ref( io_service ) );

  // Sleep for 10 seconds, then shutdown the server.
  std::cout << "Stopping service in 10 seconds..." << std::endl;
  boost::this_thread::sleep( boost::posix_time::seconds( 10 ) );
  std::cout << "Stopping service now!" << std::endl;

  // Stopping the io_service is a non-blocking call.  The threads that are
  // blocked on io_service::run() will try to return as soon as possible, but
  // they may still be in the middle of a handler.  Thus, perform a join on 
  // the server thread to guarantee a block occurs.
  io_service.stop();

  std::cout << "Waiting on server thread..." << std::endl;
  server_thread.join();
  std::cout << "Done waiting on server thread." << std::endl;

  return 0;
}

実行中に、2 つの接続を開きました。出力は次のとおりです。

10 秒後にサービスを停止します...
接続待ち
ソケットで物事を行う...
ソケットの使用が完了し、別の接続の準備が整いました。
接続待ち
ソケットで物事を行う...
ソケットの使用が完了し、別の接続の準備が整いました。
接続待ち
ただいまサービス停止中!
サーバースレッドで待機中...
サーバー スレッドが終了しています。
サーバー スレッドでの待機が完了しました。
于 2012-06-25T18:35:23.323 に答える
4

その場合は、localhost で一時的なクライアント接続を開くことができます。これにより、それが起動されます。パブからサーバーをシャットダウンできるように、特別なメッセージを送信することもできます-そのためのアプリがあるはずです:)

于 2012-06-25T16:04:27.463 に答える
4

終了する時間になったというイベントを受け取ったら、 を呼び出すことができますacceptor.cancel()。これにより、保留中の承認がキャンセルされます (エラー コードはoperation_canceled)。一部のシステムでは、close()安全のためにアクセプターも必要になる場合があります。

于 2012-06-25T15:51:56.373 に答える
1

ネイティブ ハンドルと SHUT_RD オプションを使用してシャットダウンを呼び出すだけで、既存の受信 (受け入れ) 操作をキャンセルできます。

于 2016-12-30T18:53:10.680 に答える