2017-01-14 5 views
0

[免責事項]私はブーストするのが新しいです。async_sendデータが送信されていません

ブースト:: ASIOにみると、以下の機能を持つ単純な非同期TCPサーバーを作成してみました:データを受信した場合は接続した場合、データ

  • を受ける13
  • ポートで接続をリッスン

    1. を==時間は、その後、事前に定義された文字列(「何か他のものが要求された」)

    問題を返す他に、現在の日時を返しますが を、 async_sendを使ってデータを送信すると、接続を受け入れてデータを受信しますが、エラーはなく、bytes_transferredの値は正しいですが、クライアント側で空のデータを受け取ります。

    handle_accept(handle_readの代わりに)からデータを送信しようとすると、正常に動作します。

    実装: 私はブーストASIOのチュートリアルに取り組んでhereが見つかりました: は基本的にアクセプタを開始し、リスニング開始すること、tcp_serverオブジェクトをインスタンス化します。下図のように:

    int main() 
    { 
        try 
        { 
         boost::asio::io_service io_service; 
         tcp_server server(io_service); 
         io_service.run(); 
        } 
        catch (std::exception& e) 
        { 
         std::cerr << e.what() << std::endl; 
        } 
    
        return 0; 
    } 
    

    とtcp_server中を:

    class tcp_server 
    { 
    public: 
        tcp_server(boost::asio::io_service& io_service) 
         : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13)) 
        { 
         start_accept(); 
        } 
    
    private: 
        void start_accept() 
        { 
         using std::cout; 
         tcp_connection::pointer new_connection = 
          tcp_connection::create(acceptor_.get_io_service()); 
    
         acceptor_.async_accept(new_connection->socket(), 
          boost::bind(&tcp_server::handle_accept, this, new_connection, 
           boost::asio::placeholders::error)); 
         cout << "Done"; 
        } 
        ... 
    } 
    

    接続が承認されると、以下のように、私はそれを処理しています:

    以下
    void handle_accept(tcp_connection::pointer new_connection, 
         const boost::system::error_code& error) 
    { 
        if (!error) 
        { 
         new_connection->start(); 
        } 
    
        start_accept(); 
    } 
    

    tcp_connection::start()方法であり、

    void start() 
    { 
    
        boost::asio::async_read(socket_, boost::asio::buffer(inputBuffer_), 
         boost::bind(&tcp_connection::handle_read, shared_from_this(), 
          boost::asio::placeholders::error, 
          boost::asio::placeholders::bytes_transferred)); 
    
        /* the snippet below works here - but not in handle_read 
        outputBuffer_ = make_daytime_string(); 
    
        boost::asio::async_write(socket_, boost::asio::buffer(outputBuffer_), 
         boost::bind(&tcp_connection::handle_write, shared_from_this(), 
          boost::asio::placeholders::error, 
          boost::asio::placeholders::bytes_transferred));*/ 
    } 
    

    そしてhandle_readに:

    void handle_read(const boost::system::error_code& error, size_t bytes_transferred) 
    { 
        outputBuffer_ = make_daytime_string(); 
        if (strcmp(inputBuffer_, "time")) 
        { 
         /*this does not work - correct bytes_transferred but nothing shown on receiving end */ 
         boost::asio::async_write(socket_, boost::asio::buffer(outputBuffer_), 
          boost::bind(&tcp_connection::handle_write, shared_from_this(), 
           boost::asio::placeholders::error, 
           boost::asio::placeholders::bytes_transferred)); 
        } 
        else 
        { 
         outputBuffer_ = "Something else was requested";//, 128); 
         boost::asio::async_write(socket_, boost::asio::buffer(outputBuffer_), 
          boost::bind(&tcp_connection::handle_write, shared_from_this(), 
           boost::asio::placeholders::error, 
           boost::asio::placeholders::bytes_transferred)); 
        } 
    } 
    

    handle_writeを以下に示します。

    void handle_write(const boost::system::error_code& error, 
        size_t bytes_transferred) 
    { 
        if (!error) 
        { 
         std::cout << "Bytes transferred: " << bytes_transferred; 
         std::cout << "Message sent: " << outputBuffer_; 
        } 
        else 
        { 
         std::cout << "Error in writing: " << error.message(); 
        } 
    } 
    

    注意をhandle_writeに関する次の(、これは本当に奇妙なことです):

    • なしありエラー
    • bytes_transferred変数には、corr ECT値
    • outputBuffer_はそれにもかかわらず、パッケージは、(データが関係している限り)、クライアント側(Packet Sender)が空で受信

    (AS handle_readで設定)が正しい値を有します。

    完全なコードはhereです。

  • +0

    あなたが空のデータを受け取ったとすると、0バイト(eof)の受信が完了するか、async_readが完了しないことを意味しますか?なぜハンドラ関数のエラーコードをチェックしていないのですか? –

    +0

    @RichardHodges "Packet Sender"アプリケーションをクライアントとして使用しています。受信したtcpパケットのデータ部分を見ると、それは空(データ部分なし)です。上記のコードでは、inputBuffer_が正しいことを明確にしたいと思います。私はエラーをチェックしていません、私はEOF(これは私が普通だと思う、ストリーム全体を読んでいる)を受け取っていたからです。私はEOF以外のすべてのエラーをチェックします。私はまた、handle_writeを含めるように私の質問を編集します。 – Lefteris

    +0

    asio :: async_readとeofエラーに注意してください。 async_readフリー関数は合成された操作です。 bytes_transferredが非ゼロであるとき、eofを示すことが可能である。この場合、データを受け入れ、必要に応じてeofアクションを実行する必要があります(そうでない場合は、次の読み込み時に0バイトで別のeofエラーが発生するため、このオプションのステップをスキップするかどうかは関係ありません) 。 –

    答えて

    0

    質問で提供されたコメントの集団的な助けを借りて問題を解決しました。私が経験していた動作は、async_readの機能のためでした。具体的boost asio documentationで読み取り:

    この関数は非同期ストリームからデータのバイト の一定数を読み出すために使用されます。関数呼び出しは常に即座に戻ります。 次 のいずれかの条件に該当するまで、非同期処理が継続されます:

    • 供給バッファがいっぱいです。つまり、転送されるバイトはバッファサイズの合計に等しくなります。
    • エラーが発生しました。

    入力を読み取るために使用していたinputBuffer_は、128文字の配列でした。私が使用していたクライアントは、実際のデータ(パディングなし)のみを転送するため、接続がクライアントによって閉じられるまで(または128バイトのデータが転送されるまで)async_readは返されません。接続がクライアントによって閉じられたとき、要求されたデータを返送する方法はありませんでした。これは@ Arunmuの単純なpython tcpクライアント(なぜなら、彼は常に128バイトのデータを送信していたからです)と一緒に働いていたからです。

    • tcp_connection::startで:

      は(完全に動作するコードは参照用hereを供給されている)私は、次の変更を加え、問題を解決するには、私は今、入ってくるデータを読み取るためにasync_read_untilを使用しています(とよう\nを使用しますデリミタ)。入力はboost::asio::streambufに格納されます。 async_readは、デリミタが見つかるか、エラーが発生した場合に復帰することが保証されています。したがって、同時に複数のasync_writeを発行する機会はありません。
    • handle_read:エラーチェックが含まれているため、デバッグがはるかに簡単です。
    1

    完全なテストプログラム(C++ 14)。受信に応答するときの非同期バッファリングの処理に注意してください - すでに送信中の送信があるかもしれません。

    #include <boost/asio.hpp> 
    #include <thread> 
    #include <future> 
    #include <vector> 
    #include <array> 
    #include <memory> 
    #include <mutex> 
    #include <condition_variable> 
    #include <iterator> 
    #include <iostream> 
    
    namespace asio = boost::asio; 
    
    asio::io_service  server_service; 
    asio::io_service::work server_work{server_service}; 
    
    bool listening = false; 
    std::condition_variable cv_listening; 
    std::mutex    management_mutex; 
    
    auto const shared_query = asio::ip::tcp::resolver::query(asio::ip::tcp::v4(), "localhost", "8082"); 
    
    void client() 
    try 
    { 
        asio::io_service  client_service; 
        asio::ip::tcp::socket socket(client_service); 
    
        auto lock = std::unique_lock<std::mutex>(management_mutex); 
        cv_listening.wait(lock, [] { return listening; }); 
        lock.unlock(); 
    
        asio::ip::tcp::resolver resolver(client_service); 
        asio::connect(socket, resolver.resolve(shared_query)); 
        auto s = std::string("time\ntime\ntime\n"); 
        asio::write(socket, asio::buffer(s)); 
        socket.shutdown(asio::ip::tcp::socket::shutdown_send); 
    
        asio::streambuf sb; 
        boost::system::error_code sink; 
        asio::read(socket, sb, sink); 
        std::cout << std::addressof(sb); 
        socket.close(); 
        server_service.stop(); 
    } 
    catch(const boost::system::system_error& se) 
    { 
        std::cerr << "client: " << se.code().message() << std::endl; 
    } 
    
    struct connection 
        : std::enable_shared_from_this<connection> 
    { 
        connection(asio::io_service& ios) 
         : strand_(ios) 
        { 
    
        } 
    
        void run() 
        { 
         asio::async_read_until(socket_, buffer_, "\n", 
               strand_.wrap([self = shared_from_this()](auto const&ec, auto size) 
         { 
          if (size == 0) 
          { 
           // error condition 
           boost::system::error_code sink; 
           self->socket_.shutdown(asio::ip::tcp::socket::shutdown_receive, sink); 
          } 
          else { 
           self->buffer_.commit(size); 
           std::istream is(std::addressof(self->buffer_)); 
           std::string str; 
           while (std::getline(is, str)) 
           { 
            if (str == "time") { 
             self->queue_send("eight o clock"); 
            } 
           } 
           self->run(); 
          } 
         })); 
        } 
    
        void queue_send(std::string s) 
        { 
         assert(strand_.running_in_this_thread()); 
         s += '\n'; 
         send_buffers_pending_.push_back(std::move(s)); 
         nudge_send(); 
        } 
    
        void nudge_send() 
        { 
         assert(strand_.running_in_this_thread()); 
         if (send_buffers_sending_.empty() and not send_buffers_pending_.empty()) 
         { 
          std::swap(send_buffers_pending_, send_buffers_sending_); 
          std::vector<asio::const_buffers_1> send_buffers; 
          send_buffers.reserve(send_buffers_sending_.size()); 
          std::transform(send_buffers_sending_.begin(), send_buffers_sending_.end(), 
              std::back_inserter(send_buffers), 
          [](auto&& str) { 
           return asio::buffer(str); 
          }); 
          asio::async_write(socket_, send_buffers, 
               strand_.wrap([self = shared_from_this()](auto const& ec, auto size) 
          { 
           // should check for errors here... 
           self->send_buffers_sending_.clear(); 
           self->nudge_send(); 
          })); 
         } 
        } 
    
        asio::io_service::strand strand_; 
        asio::ip::tcp::socket socket_{strand_.get_io_service()}; 
        asio::streambuf   buffer_; 
    
        std::vector<std::string> send_buffers_pending_; 
        std::vector<std::string> send_buffers_sending_; 
    }; 
    
    void begin_accepting(asio::ip::tcp::acceptor& acceptor) 
    { 
        auto candidate = std::make_shared<connection>(acceptor.get_io_service()); 
        acceptor.async_accept(candidate->socket_, [candidate, &acceptor](auto const& ec) 
        { 
         if (not ec) { 
          candidate->run(); 
          begin_accepting(acceptor); 
         } 
        }); 
    } 
    
    void server() 
    try 
    { 
        asio::ip::tcp::acceptor acceptor(server_service); 
        asio::ip::tcp::resolver resolver(server_service); 
    
        auto first = resolver.resolve(shared_query); 
        acceptor.open(first->endpoint().protocol()); 
        acceptor.bind(first->endpoint()); 
    
        acceptor.listen(); 
    
        begin_accepting(acceptor); 
    
        auto lock = std::unique_lock<std::mutex>(management_mutex); 
        listening = true; 
        lock.unlock(); 
        cv_listening.notify_all(); 
    
    
        server_service.run(); 
    
    } 
    catch(const boost::system::system_error& se) 
    { 
        std::cerr << "server: " << se.code().message() << std::endl; 
    } 
    
    int main() 
    { 
    
        using future_type = std::future<void>; 
    
        auto stuff = std::array<future_type, 2> {{std::async(std::launch::async, client), 
                   std::async(std::launch::async, server)}}; 
    
        for (auto& f : stuff) f.wait(); 
    
    } 
    
    1

    このコードには複数の問題があります。それらのいくつかは、あなたの問題を担当することがあります。

    • TCPはパケットのない定義がないので、あなたが今までhandle_readに一度timeを受けるという保証はありません。あなたはそれのためのstatemachineとbytes_transferred情報を尊重する必要があります。メッセージの一部のみを受信した場合は、正しいオフセットで続行する必要があります。あるいは、バイト単位の正確な読み込みや行の読み込みなどのasioユーティリティ関数を使用することもできます。
    • 最後に、受信したデータを実際にstrcmpと比較するべきではありません。それはリモコンが接続上でヌルターミネータを送信する場合にのみ機能します - そうですか?
    • エラーが発生したかどうかはチェックしませんが、それは他のエラーで現れる可能性があります。
    • shart timespanで複数のデータフラグメントを受け取った場合、複数の同時非同期書き込みを発行する可能性があります。これはasioでは無効です。
    • さらに重要なことは、送信中に送信バッファ(outputBuffer_)を変更することです。これは、未定義の動作につながります。 asioはもはや有効ではないメモリを書き込もうとするかもしれません。
    +0

    ありがとう@ Matthias247 - 私は隆起点のそれぞれを見て、うまくいけば私はそれの底に到達します。あなたの第2弾と第3弾は簡単です。第4弾は時間を費やす必要があります。 5日目は私にはあまり明確ではありませんが、もう少し説明できますか?第1の点も明確です。 – Lefteris

    +1

    OK、ポイント5:boost :: asio :: buffer(outputBuffer_)を使用してboost asioに送信するデータを指定した場合、パフォーマンス上の理由からasioはデータをコピーして内部に保存しません。それは、データへのポインタとデータの長さのようなものを意味するバッファラッパー構造体をコピーするだけです。あなたは、データが操作の全期間にわたって生き続けるという責任を負います。非同期書き込み操作中にasioに与えた位置のメモリが変更された場合、その未定義の動作が発生します。 – Matthias247

    +1

    ここでstd :: stringを使用すると、操作の開始時に文字列内部バッキングメモリにasioポインタを渡します。文字列( 'outputBuffer _ = ...')を再割り当てすると、std :: stringはその位置のデータを変更するか、新しいバッキングバッファを割り当てて古いものを割り当て解除します。古いものへのポインタは無効です。 – Matthias247

    関連する問題