簡體   English   中英

Boost.Asio設計的示例莫名其妙地阻塞了

[英]Boost.Asio contrived example inexplicably blocking

我已經廣泛使用Bo​​ost.Asio,但是遇到了我不理解的單元測試問題。 我將問題簡化為一個非常人為的示例:

#include <string>
#include <chrono>
#include <thread>
#include <mutex>
#include <condition_variable>

#include <boost/asio.hpp>
#define BOOST_TEST_MODULE My_Module
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>

using namespace std::string_literals;
using namespace std::chrono_literals;

namespace BA = boost::asio;
namespace BAI = BA::ip;

BOOST_AUTO_TEST_CASE(test)
{
    std::mutex m;
    std::condition_variable cv;

    BA::io_service servicer;
    auto io_work = std::make_unique<BA::io_service::work>(servicer);

    auto thread = std::thread{[&]() {
        servicer.run();
    }};

    auto received_response = false;

    auto server_buf = std::array<std::uint8_t, 4096>{};
    auto server_sock = BAI::tcp::socket{servicer};
    auto acceptor = BAI::tcp::acceptor{servicer,
                                       BAI::tcp::endpoint{BAI::tcp::v4(), 20123}};
    acceptor.async_accept(server_sock, [&](auto&& ec) {
        if (ec) {
            BOOST_TEST_MESSAGE(ec.message());
        }
        BOOST_REQUIRE(!ec);

        BOOST_TEST_MESSAGE("Accepted connection from " << server_sock.remote_endpoint() <<
                           ", reading...");

        BA::async_read(server_sock,
                       BA::buffer(server_buf),
                       [&](auto&& ec, auto&& bytes_read){
            std::unique_lock<decltype(m)> ul(m);
            received_response = true;

            if (ec) {
                BOOST_TEST_MESSAGE(ec.message());
            }
            BOOST_REQUIRE(!ec);

            const auto str = std::string{server_buf.begin(),
                                         server_buf.begin() + bytes_read};
            BOOST_TEST_MESSAGE("Read: " << str);

            ul.unlock();
            cv.notify_one();
        });
    });

    const auto send_str = "hello"s;
    auto client_sock = BAI::tcp::socket{servicer, BAI::tcp::v4()};
    client_sock.async_connect(BAI::tcp::endpoint{BAI::tcp::v4(), 20123},
                              [&](auto&& ec) {
        if (ec) {
            BOOST_TEST_MESSAGE(ec.message());
        }
        BOOST_REQUIRE(!ec);

        BOOST_TEST_MESSAGE("Connected...");
        BA::async_write(client_sock,
                        BA::buffer(send_str),
                        [&](auto&& ec, auto&& bytes_written) {
            if (ec) {
                BOOST_TEST_MESSAGE(ec.message());
            }
            BOOST_REQUIRE(!ec);

            BOOST_TEST_MESSAGE("Written " << bytes_written << " bytes");
        });
    });

    std::unique_lock<decltype(m)> ul(m);
    cv.wait_for(ul, 2s, [&](){ return received_response; });
    BOOST_CHECK(received_response);

    io_work.reset();
    servicer.stop();
    if (thread.joinable()) {
        thread.join();
    }
}

我用它編譯:

g++ -std=c++17 source.cc -l boost_unit_test_framework -pthread -l boost_system -ggdb

輸出為:

Accepted connection from 127.0.0.1:51688, reading...
Connected...
Written 5 bytes

然后超時。

通過調試器運行表明,永遠不會調用async_read處理程序。 在似乎什么都沒做的階段中暫停執行,表明主線程在condition_variablecv )上等待,而io_service線程在epoll_wait

我似乎陷入僵局,但看不到如何。

這是函數定義的工作方式,它確切地等待緩沖區具有空間的字節數( http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/async_read /overload1.html )。

請嘗試以下一種方法: http : //www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/async_read/overload2.html

您可以提供一個回調函數來決定讀取是否完成,並且可以包括在編寫者編寫其消息后(如果您確定了無死鎖的方式)等待並檢查另一個通道提供的長度(或者在正確的消息之前。

添加此完成條件使其起作用:

[&](auto&& ec, auto&& bytes_read){
    return bytes_read < 5 ? 5 - bytes_read : 0;
},

@codeshot提供的答案是正確的,但這是幾種解決方案之一-最合適的解決方案完全取決於您在TCP連接上使用的協議。

例如,在傳統的鍵長-值樣式協議中,您將執行兩次讀取:

  1. 使用boost::asio::async_read (或等效方法)讀入固定長度緩沖區以獲得固定長度報頭
  2. 使用標頭指定的長度來創建所需大小的緩沖區,並使用它重復步驟1

聊天服務器示例代碼中有一個很好的示例。

如果您使用的是HTTP或RTSP(后者是我想做的),那么您就不知道會有多少數據輸入,您所關心的只是接收到一個數據包中的數據(我知道這過於簡單了)由於響應中包含Content-Length標頭,分塊的傳輸編碼等,請耐心等待)。 為此,您需要async_read_some (或等效方法),請參見HTTP服務器示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM