C++ boost::asio::io\u context::run\u one\u for()无法发送大缓冲区

C++ boost::asio::io\u context::run\u one\u for()无法发送大缓冲区,c++,boost,asio,C++,Boost,Asio,我需要获得同步I/O,但具有以下功能: 被其他线程中断 支持超时 因此,我使用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

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

  • 被其他线程中断
  • 支持超时
  • 因此,我使用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;
    }
    
    我认为原因是boost::asio::io_context::run_one_for()的实现。这就是(boost 1.67):

    模板
    std::size\u t io\u context::运行一个(
    常数时间::持续时间和相对时间)
    {
    返回此->运行一次直到(计时::稳定时钟::现在()+rel\u时间);
    }
    模板
    std::size\u t io\u context::运行一个直到(
    常数计时:时间(点和绝对时间)
    {
    typename Clock::time_point now=Clock::now();
    同时(现在计时::秒(1))
    相对时间=时间:秒(1);
    boost::system::error_code ec;
    std::size\u t s=impl\uu.wait\u one(
    静态施法(计时::持续施法)<
    计时:微秒>(相对时间).count()),秒);
    boost::asio::detail::抛出错误(ec);
    如果(s | | impl|uu.stopped())
    返回s;
    现在=时钟::现在();
    }
    返回0;
    }
    
    请注意1秒的限制。我认为发送大缓冲区需要超过1秒,执行就会停止。我可以按64k字节的顺序执行
    Write()
    ,它可以正常工作。但我认为如果连接无法每秒发送64k字节,它将失败。所以,我需要一个更好的解决方案

    我认为原因是boost::asio::io_context::run_one_for()的实现。这就是(boost 1.67):

    不是。原因是
    boost::asio::[async|][read | write][|.*]
    是组合操作<代码>运行一个[*]只运行一个处理程序,因此它不会完成一个组合的异步操作

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

    您可以将异步操作包装在本地io_上下文中,并使用截止日期将其运行到完成(因此,
    run_for
    ,而不是
    run_one_for

    您可以从中收集到类似的概念-尽管严格地(很多)使用
    run\u for(…)
    而不是
    run\u one()
    (出于上述原因。在我编写另一个答案时,
    run\u for()
    不存在,我可能不得不更新该答案,因为我可能错过了当时组合操作的问题)。

    在我的PerformIO()中使用您的wait_操作()。很好。
    // tcp::socket Socket;
    // char const *Buffer;
    // size_t BufferSize = 1000000;
    Write(Socket, asio::buffer(Buffer, BufferSize))
    
    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;
    }