0

boost::asio同じコンピューター上で実行される node.js TCP サーバー アプリケーションへの同期 TCP ソケット接続を確立するために使用しています。Embarcadero 統合ブースト バージョン 1.50 を使用する Windows ビルド 64 ビット アプリケーションで Embarcadero RAD studio XE4 を使用しています。

node.js TCP サーバーがシャットダウンする場合を除いて、問題なく動作します。これが発生すると、C++ クライアント アプリケーションはソケットからの読み取り時に切断を検出しません。ただし、ソケットに書き込むと切断が検出されます。

私の現在のコードは、ブーストのドキュメントとSOに関するさまざまな回答を理解しようとした後に作成されました。コードの読み取り部分は次のとおりです(簡潔にするためにエラー コードのチェックは省略しています)。

boost::system::error_code ec;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port);

ec = s.connect(ep,ec);
std::size_t bytes_available = s.available(ec);
std::vector<unsigned char> data(bytes_available,0);

size_t read_len = boost::asio::read(s,  boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec);

if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
{
    // disconnection
    break;
}

これは、ループ内の独自のスレッドで実行され、プログラムがシャットダウンされるまで絶えずデータをポーリングするため、優れたシステムではありません。通常read()、データがない場合はソケットで実行しませんが、この場合は実行します。これは、すべてのドキュメントから、ソケットへの読み取りまたは書き込みを実行するときにのみソケットの切断が検出されると思われるためです。問題は、node.js アプリケーションがシャットダウンしたときに、上記のコードが切断をまったく検出しないことです。私が書いている場合はそれを検出します(コードの書き込み部分は、検出のために読み取りと同じboost::asio::error`エラーを使用しています)が、読み取り時には検出しません。

明らかに、利用可能なバイト量よりも大きいバイト量の読み取りを実行することはできません。そうしないと、スレッドがブロックされ、後でスレッド ループで書き込みを実行できなくなります。

エラー状態を検出するための別の特定のブースト エラー コードがありませんか? または、ゼロ長の読み取りに特に問題がありますか。その場合、他に利用できるオプションはありますか?

現在、node.jsサーバーが特定のメッセージをソケットに書き出すようにしています。これは、シャットダウン時に検出され、クライアントエンドを自分で閉じます。ただし、これはちょっとしたハックであり、可能であれば、切断を検出するためのクリーンな方法を希望します。

4

1 に答える 1

5

通常、Boost.Asio のread()関数は次のいずれかの場合に戻ります。

  • バッファがいっぱいです。
  • 完全な条件が満たされました。
  • エラーが発生しました。

これらの条件がチェックされる順序は指定されていません。しかし、前回実装を見たとき、Boost.Asio はソケットからの読み取りを試行する前に、ストリームでゼロバイトを読み取る操作をノーオペレーションとして扱いました。したがって、サイズ 0 のバッファーがいっぱいであると見なされるため、ファイルの終わりエラーは発生しません。

非同期操作を使用すると、より良い結果とスケーラビリティが得られる可能性がありますが、非ブロッキング方法での切断の検出は、同期操作でも実現できます。デフォルトでは、同期操作はブロックされていますが、この動作は socket::non_blocking()関数を介して制御できます。ドキュメントには次のように記載されています。

の場合true、ソケットの同期操作はboost::asio::error::would_block、要求された操作をすぐに実行できない場合に失敗します。の場合false、同期操作は完了するまでブロックされます。

したがって、同期操作がブロックしないように設定されていて、読み取り操作が最小 1 バイトの読み取りを試行する場合、ブロックしない方法で切断が観察される可能性があります。


read()切断を検出するノンブロッキング同期操作を示す完全な例を次に示します。印刷メッセージを調整するために、操作がブロックされた場合 (つまり、接続はあるが読み取り可能なデータがない場合) にスリープを実行することにしました。

#include <algorithm>
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    std::cerr << "Usage: <port>\n";
    return 1;
  }

  // Create socket and connet to local port.
  namespace ip = boost::asio::ip;
  boost::asio::io_service io_service;
  ip::tcp::socket socket(io_service);
  socket.connect(ip::tcp::endpoint(
      ip::address::from_string("127.0.0.1"), std::atoi(argv[1])));

  // By setting the socket to non-blocking, synchronous operations will
  // fail with boost::asio::error::would_block if they cannot immediately
  // perform the requested operation.
  socket.non_blocking(true);

  // Synchronously read data.
  std::vector<char> data;
  boost::system::error_code ec;
  for (;;)
  {
    // Resize the buffer based on the amount of bytes available to be read.
    // Guarantee that the buffer is at least 1 byte, as Boost.Asio treats
    // zero byte read operations as no-ops.
    data.resize(std::max<std::size_t>(1, socket.available(ec)));

    // Read all available data.
    std::size_t bytes_transferred =
        boost::asio::read(socket, boost::asio::buffer(data), ec);

    // If no data is available, then continue to next iteration.
    if (bytes_transferred == 0 && ec == boost::asio::error::would_block)
    {
      std::cout << "no data available" << std::endl;
      boost::this_thread::sleep_for(boost::chrono::seconds(3));
      continue;
    }            

    std::cout << "Read: " << bytes_transferred << " -- ";
    if (bytes_transferred)
    {
        std::cout.write(&data[0], bytes_transferred);
        std::cout << " -- ";
    }
    std::cout << ec.message() << std::endl;

    // On error, such as a disconnect, exit the loop.
    if (ec && ec != boost::asio::error::would_block)
    {
      break;
    }
  }
}

次の出力は、「test」、「more testing」と書き込み、接続を閉じたサーバーにサンプル プログラムを接続することによって生成されました。

no data available
Read: 4 -- test -- Success
no data available
Read: 12 -- more testing -- Success
Read: 0 -- End of file
于 2014-04-01T20:52:15.397 に答える