boost :: asio :: io_context :: run_one_for()无法发送大缓冲区

时间:2018-08-16 13:51:43

标签: c++ boost asio

我需要获取同步I / O,但具有以下功能:

  1. 被其他线程中断
  2. 支持超时

因此,我使用Boost.Asio的异步I / O,并通过boost :: asio :: io_context :: run_one_for()执行它。 有一个实现解析,连接,读取和写入的示例。

long const DefaultTimeout = 50;

namespace asio = boost::asio;
using boost::asio::ip::tcp;

template <typename T>
void PerformIO(T &Object, long Timeout)
{
    auto &Context = Object.get_executor().context();
    Context.reset();
    if (!Context.run_one_for(std::chrono::seconds(Timeout)))
        throw std::exception("I/O operation was timed out");
}

template <typename T, typename TSuccessFlag>
void PerformIO(T &Object, long Timeout, TSuccessFlag &Success)
{
    PerformIO(Object, Timeout);
    boost::this_thread::interruption_point();
    if (!Success)
        throw std::exception("I/O operation was not successful");
}

tcp::resolver::results_type Resolve(tcp::resolver &Resolver, std::string const &Host, std::string const &Port)
{
    bool Resolved = false;
    tcp::resolver::results_type Endpoints;
    Resolver.async_resolve(Host, Port,
        [&](boost::system::error_code const &Error, boost::asio::ip::tcp::resolver::results_type Results){ Endpoints = Results; Resolved = true; });
    PerformIO(Resolver, DefaultTimeout, Resolved);
    if (Endpoints.begin() == Endpoints.end())
        throw std::exception("Not resolved");
    return Endpoints;
}

template <typename T>
void Connect(tcp::socket &Socket, T const &Endpoints)
{
    bool Connected = false;
    asio::async_connect(Socket, Endpoints,
        [&](boost::system::error_code const &Error, boost::asio::ip::tcp::endpoint const &Endpoint){ Connected = true; });
    PerformIO(Socket, DefaultTimeout, Connected);
}

template <typename T>
size_t ReadSome(tcp::socket &Socket, T &Buffers)
{
    size_t Bytes = 0;
    asio::async_read(Socket, Buffers, asio::transfer_at_least(1),
        [&](boost::system::error_code const &Error, std::size_t BytesTransferred){ Bytes += BytesTransferred; });
    PerformIO(Socket, DefaultTimeout, Bytes);
    return Bytes;
}

template <typename T>
size_t Write(tcp::socket &Socket, T &Buffers)
{
    size_t Bytes = 0;
    asio::async_write(Socket, Buffers,
        [&](boost::system::error_code const &Error, std::size_t BytesTransferred){ Bytes += BytesTransferred; });
    PerformIO(Socket, DefaultTimeout, Bytes);
    return Bytes;
}

对于小数据它可以正常工作,但是如果我尝试发送大数据缓冲区,则失败:

// tcp::socket Socket;
// char const *Buffer;
// size_t BufferSize = 1000000;
Write(Socket, asio::buffer(Buffer, BufferSize))

我认为原因是boost :: asio :: io_context :: run_one_for()的实现。在那里(提升1.67):

template <typename Rep, typename Period>
std::size_t io_context::run_one_for(
    const chrono::duration<Rep, Period>& rel_time)
{
  return this->run_one_until(chrono::steady_clock::now() + rel_time);
}

template <typename Clock, typename Duration>
std::size_t io_context::run_one_until(
    const chrono::time_point<Clock, Duration>& abs_time)
{
  typename Clock::time_point now = Clock::now();
  while (now < abs_time)
  {
    typename Clock::duration rel_time = abs_time - now;
    if (rel_time > chrono::seconds(1))
      rel_time = chrono::seconds(1);

    boost::system::error_code ec;
    std::size_t s = impl_.wait_one(
        static_cast<long>(chrono::duration_cast<
          chrono::microseconds>(rel_time).count()), ec);
    boost::asio::detail::throw_error(ec);

    if (s || impl_.stopped())
      return s;

    now = Clock::now();
  }

  return 0;
}

请注意1秒的限制。我认为发送大缓冲区需要1秒钟以上,并且执行停止。我可以按64k字节顺序执行Write(),并且可以正常工作。但是我认为如果连接无法每秒发送64k字节,它将失败。因此,我需要一个更好的解决方案。

1 个答案:

答案 0 :(得分:1)

  

我认为原因是boost :: asio :: io_context :: run_one_for()的实现。在那里(提升1.67):

不。原因是boost::asio::[async_][read|write][_.*]是组合操作。 run_one[_*]仅运行一个处理程序,因此无法完成组合的异步操作。

还要注意,boost::asio::write是一个同步操作,与周围的任何逻辑无关,因此您的超时甚至不会起作用。

您只需将异步操作包装在本地io_context中,然后使用截止日期将其运行至完成(因此,run_for而不是run_one_for)。

您可以从How to simulate boost::asio::write with a timeout中收集类似的概念-尽管使用run_for(...)而不是run_one()严格(好多)(出于上述原因)。我写了另一个答案,run_for()不存在,我可能还是不得不更新该答案,因为那时我可能已经错过了组合操作的问题。