Boost.Asio设计的例子令人费解地阻塞

时间:2017-08-28 20:52:04

标签: c++ boost-asio

我已经广泛使用过Boost.Asio,但是我遇到了一个我不明白的单元测试问题。我把这个问题简化为一个非常人为的例子:

Arduino: 1.8.4 (Windows 8.1), Board: "Arduino Pro or Pro Mini, ATmega168 (3.3V, 8 MHz)"

Archiving built core (caching) in: C:\Users\pxb07\AppData\Local\Temp\arduino_cache_814882\core\core_arduino_avr_pro_cpu_8MHzatmega168_0c812875ac70eb4a9b385d8fb077f54c.a
Sketch uses 3284 bytes (22%) of program storage space. Maximum is 14336 bytes.
Global variables use 335 bytes (32%) of dynamic memory, leaving 689 bytes for local variables. Maximum is 1024 bytes.
C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avrdude -CC:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf -v -patmega168 -carduino -PCOM8 -b19200 -D -Uflash:w:C:\Users\pxb07\AppData\Local\Temp\arduino_build_381498/SoftwareSerialExample.ino.hex:i 

avrdude: Version 6.3, compiled on Jan 17 2017 at 12:00:53
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:\Program Files (x86)\Arduino\hardware\tools\avr/etc/avrdude.conf"

         Using Port                    : COM8
         Using Programmer              : arduino
         Overriding Baud Rate          : 19200
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0x28
avrdude: stk500_recv(): programmer is not responding
avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x28

avrdude done.  Thank you.

Problem uploading to board.  See http://www.arduino.cc/en/Guide/Troubleshooting#upload for suggestions.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.
我编译的

#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_readcondition_variable)并且cv线程在io_service

我似乎陷入僵局,但无法理解。

2 个答案:

答案 0 :(得分:4)

这是函数定义的工作方式,它等待缓冲区为(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

你可以给出一个回调来决定读取是否完整,并且可以包括在编写器写入消息后等待并检查另一个通道提供的长度(如果你已经确定了无死锁的方式) )或恰好在信息正确之前。

添加此完成条件使其有效:

A.sort((a,b) => Math.abs(a) - Math.abs(b))

答案 1 :(得分:1)

@codeshot提供的答案是正确的,但它是几种解决方案中的一种 - 最合适的完全取决于您在TCP连接中使用的协议。

例如,在传统的Key-Length-Value样式协议中,您将执行两次读取:

  1. 使用boost::asio::async_read(或等效内容)读入固定长度缓冲区以获取固定长度标题
  2. 使用标题指定的长度创建所需大小的缓冲区,并使用它重复步骤1
  3. chat server example code中有一个很好的例子。

    如果您使用的是HTTP或RTSP(后者就是我想要做的事情),那么您就不知道数据的来源有多少,您所关心的只是收到数据包的价值数据(我知道这是一个过于简单化的问题,因为响应中的Content-Length标题,分块转移编码等等,但是请耐心等待。为此,您需要async_read_some(或等效),请参阅HTTP server example