質問

保留中の操作を(切断せずに)キャンセルする方法や、ブーストライブラリ関数のタイムアウトを設定する方法はありますか?

つまりboost asioでブロッキングソケットのタイムアウトを設定したいですか?

socket.read_some(boost :: asio :: buffer(pData、maxSize)、error _);

例:ソケットから一部を読み取りたいが、10秒が経過するとエラーをスローしたい。

役に立ちましたか?

解決

Linux / BSDでは、ソケットでのI / O操作のタイムアウトはオペレーティングシステムによって直接サポートされています。このオプションは、 setsocktopt()で有効にできます。 boost :: asio がそれを設定するメソッドを提供するか、ソケットスクリプターを公開して直接設定できるかどうかはわかりません。後者のケースは実際には移植性がありません。

完全を期すために、manページの説明を次に示します。

  

SO_RCVTIMEO および SO_SNDTIMEO

          Specify the receiving or sending  timeouts  until  reporting  an
          error.  The argument is a struct timeval.  If an input or output
          function blocks for this period of time, and data has been  sent
          or  received,  the  return  value  of  that function will be the
          amount of data transferred; if no data has been transferred  and
          the  timeout has been reached then -1 is returned with errno set
          to EAGAIN or EWOULDBLOCK just as if the socket was specified  to
          be  non-blocking.   If  the timeout is set to zero (the default)
          then the operation  will  never  timeout.   Timeouts  only  have
          effect  for system calls that perform socket I/O (e.g., read(2),
          recvmsg(2), send(2), sendmsg(2)); timeouts have  no  effect  for
          select(2), poll(2), epoll_wait(2), etc.

他のヒント

この質問が出されたとき、ASIOには、OPが必要とすること、つまりブロッキングソケット操作などのブロッキング操作をタイムアウトさせる方法を達成する方法の例がなかったと思います。これを行う方法を正確に示すための例があります。例は長いようですが、それはよくコメントされているからです。 「ワンショット」モードのモードでioserviceを使用する方法を示します。

この例は素晴らしい解決策だと思います。ここでの他のソリューションは移植性を壊し、ioserviceを利用しません。移植性が重要ではなく、ioserviceのオーバーヘッドが非常に大きいと思われる場合--THEN-- ASIOを使用しないでください。何があっても、ioserviceを作成することになります(ほぼすべてのASIO機能はソケットに依存します)ので、それを利用してください。

ブロッキングasio tcp操作のタイムアウト

ブロッキングasio udp操作のタイムアウト

ASIOのドキュメントが更新されたため、ASIOが使用していた「落とし穴」のいくつかを克服する方法に関する新しい例を確認してください。

async_readを実行して、希望するタイムアウトのタイマーを設定することもできます。次に、タイマーが作動したら、ソケットオブジェクトでcancelを呼び出します。それ以外の場合、読み取りが発生した場合、タイマーをキャンセルできます。もちろん、io_serviceオブジェクトを使用する必要があります。

編集:これを行うコードスニペットが見つかりました

http://lists.boost.org/Archives/boost/ 2007/04 / 120339.php

私は同じ質問をしましたが、いくつかの調査の後、思いついた最も簡単でクリーンな解決策は、基礎となるネイティブソケットを取得し、読み取るデータがあるまで選択を行うことでした。 selectはタイムアウトパラメータを取ります。もちろん、ネイティブソケットの使用は、そもそもasioを使用するという点に反し始めますが、これも最もクリーンな方法のようです。私が知る限り、asioは同期使用のためにこれを簡単に行う方法を提供していません。コード:

        // socket here is:  boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr

        // Set up a timed select call, so we can handle timeout cases.

        fd_set fileDescriptorSet;
        struct timeval timeStruct;

        // set the timeout to 30 seconds
        timeStruct.tv_sec = 30;
        timeStruct.tv_usec = 0;
        FD_ZERO(&fileDescriptorSet);

        // We'll need to get the underlying native socket for this select call, in order
        // to add a simple timeout on the read:

        int nativeSocket = a_socket_ptr->native();

        FD_SET(nativeSocket,&fileDescriptorSet);

        select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

        if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout

                std::string sMsg("TIMEOUT on read client data. Client IP: ");

                sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());

                throw MyException(sMsg);
        }

        // now we know there's something to read, so read
        boost::system::error_code error;
        size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);

        ...

おそらくこれはあなたの状況に役立つでしょう。

TL; DR

socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });

完全な回答 この質問は長年にわたって何度も何度も聞かれ続けています。私がこれまで見た答えは非常に貧弱です。この質問の最初の出現の1つで、この情報をここに追加します。

ASIOを使用してネットワークコードを簡素化しようとする人は誰でも、すべての同期および非同期io関数にオプションのパラメータータイムアウトを追加するだけであれば、完全に満足するでしょう。残念なことに、これは起こりそうにありません(私の謙虚な意見では、イデオロギーの理由だけで、結局ASIOのASには理由があります)。

したがって、これらはこの貧しい猫をこれまでのところ利用できるようにする方法であり、どれも特に食欲をそそるものではありません。 200msのタイムアウトが必要だとしましょう。

1)良い(悪い)古いソケットAPI:

const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops

これらの特性に注意してください: -タイムアウトのconst int-Windowsでは、必要な型は実際にはDWORDですが、幸いなことに現在のコンパイラのセットは同じであるため、const intはWinとPosixの両方の世界で機能します。 -値には(const char *)。 Windowsではconst char *が必要ですが、Posixではconst void *が必要です。C++ではconst char *はconst void *にサイレントに変換されますが、逆は当てはまりません。

利点:動作します。ソケットAPIが古くて安定しているため、おそらく常に動作します。簡単です。速い。 欠点:技術的には、setsockoptとマクロに適切なヘッダーファイル(Winでは異なる、UNIXでも異なる)が必要になる場合がありますが、ASIOの現在の実装では、とにかくグローバル名前空間が汚染されます。タイムアウトには変数が必要です。タイプセーフではありません。 Windowsでは、ソケットが機能するためにオーバーラップモードである必要があります(現在のASIO実装では幸運にも使用されていますが、実装の詳細です)。い!

2)カスタムASIOソケットオプション:

typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });

利点:簡単です。速い。美しい(typedefを使用)。 欠点:ASIO実装の詳細に依存しますが、これは変更される可能性があります(ただし、OTOHのすべてが最終的に変更され、そのような詳細は標準化の対象となるパブリックAPIよりも変更される可能性が低くなります)。ただし、これが発生した場合は、 https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (これはもちろん、 ASIOのこの部分)または1に戻ります。

3)C ++の非同期/将来の機能を使用します。

#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
    .wait_for(std::chrono::milliseconds{ 200 });
switch (status)
    {
    case std::future_status::deferred:
    //... should never happen with std::launch::async
        break;
    case std::future_status::ready:
    //...
        break;
    case std::future_status::timeout:
    //...
        break;
    }

利点:標準。 欠点:常に新しいスレッドを(実際には)開始します。これは比較的低速です(クライアントには十分かもしれませんが、スレッドとソケットは「高価な」リソースであるため、サーバーのDoS脆弱性につながります)。 wait_forは常にコードを実行しようとせずにfuture_status :: deferredを返すため、std :: launch :: asyncの代わりにstd :: launch :: deferredを使用しないでください。

4)ASIOが規定する方法-非同期操作のみを使用します(実際には質問に対する答えではありません)。

利点:短いトランザクションのための巨大なスケーラビリティが必要ない場合、サーバーにも十分に適しています。 短所:非常に冗長です(そのため、例さえ含めません-ASIOの例を参照してください)。非同期操作とその完了ハンドラーの両方で使用されるすべてのオブジェクトの非常に慎重なライフタイム管理が必要です。実際には、非同期操作でそのようなデータを含み、使用するすべてのクラスがenable_shared_from_thisから派生することを必要としますすべてのヒープalloc / deallocがメモリバリアを使用するため、少なくとも短時間の操作の場合)スケーラビリティは約16スレッド後にテーパーダウンを開始します。

grepsedawkが述べたことに続いて。 asio docoの Timeouts セクションに、一定時間後に長時間実行される非同期操作をキャンセルする方法を示すいくつかの例があります。 Boost Asioの例非同期TCPクライアントが最も役に立ちました。

ハッピー非同期:)

元の質問の数年後でも、満足のいく答えはまだありません。

手動でselectを使用するのは良いオプションではありません

  1. ファイル記述子番号は1024未満でなければなりません
  2. チェックサムが間違っているため、FDが準備完了として誤って報告される場合があります。

常に run()するためにio_serviceを必要とする他の非同期オプションがあるかもしれないので、 io_service.run_one()を呼び出すことも悪い考えです。また、tcpクライアントのブロックに関するboostのドキュメントは理解するのが困難です。

これが私の解決策です。重要なアイデアは次のとおりです。

{
    Semaphore r_sem;
    boost::system::error_code r_ec;
    boost::asio::async_read(s,buffer,
                            [this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
                                r_ec=ec_;
                                r_sem.notify();
                            });
    if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
    {
        s.cancel();
        r_sem.wait();
        throw boost::system::system_error(boost::asio::error::try_again);
    }
    else if(r_ec)
        throw boost::system::system_error(r_ec);
}

ここでセマフォはmutexとcondition_variableです。
wait_for は、 http://en.cppreferenceによって実装されます.com / w / cpp / thread / condition_variable / wait_for

完全なコードは https://github.com/scinartにあります。 /cpplib/blob/master/include/asio.hpp
例は https://github.com/scinart/にあります。 cpplib / blob / master / test / test_asio.cpp
https://github.com/scinart/cpplib/blobのより良い例/master/test/test_SyncBoostIO.cpp

同期呼び出しをフューチャにラップして、タイムアウト(wait_timeout)で完了するまで待つことができます。

http:// www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.futures

1つのサイズですべてに適合するわけではありませんが、たとえば遅い接続タイムアウトの回避。

* nixでは、alarm()を使用するため、ソケット呼び出しはEINTRで失敗します

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top