如何使用超时模拟boost :: asio :: write

时间:2017-11-19 14:34:11

标签: sockets boost boost-asio tcp-ip

我试图用超时模拟boost::asio::write。或者你可以说,我试图使用boost::asio::async_write超时。

正如我所见,boost::asio::write会阻止所有数据写入&在另一边阅读。这种功能当然需要暂停。

所以,通过 Robert Hegner 阅读this simple answer here,其中阐述了如何使用超时进行boost::asio::async_read,我这样做是尝试使用相同的逻辑来编写

size_t write_data_with_time_out() {

    long time_out_secs = 2;

    boost::optional<boost::system::error_code> timer_result;
    boost::asio::deadline_timer timer(the_socket->get_io_service(), boost::posix_time::seconds(time_out_secs));

    timer.expires_from_now();
    timer.async_wait([&timer_result] (const boost::system::error_code& error) {
        timer_result.reset(error);
    });

    boost::optional<boost::system::error_code> write_result;
    size_t bytes_sent = 0;
    boost::asio::async_write(*the_socket, boost::asio::buffer(the_buffer_to_write, the_buffer_to_write.size()), [&write_result, &bytes_sent] (const boost::system::error_code& error, auto size_received) {

        write_result.reset(error);
        bytes_sent = size_received;
    });

    the_socket->get_io_service().reset();
    while (the_socket->get_io_service().run_one()) {

        if (write_result) {
            timer.cancel();
        }
        else if (timer_result) {
            the_socket->cancel();
        }
    }

    if (*write_result) {
        return 0;
    }

    return bytes_sent;
}

问题:
该逻辑非常适合读取,但似乎不适用于写入案例。 原因是在while (the_socket->get_io_service().run_one())两次调用the_socket->cancel()后挂起the_socket->cancel()

然而,在阅读案例中,boost::asio::async_write也会被调用两次&amp;并没有挂在第三个循环的时间和&amp;退出。因此没有阅读问题。

问题:
我的理解是错误的,相同的超时逻辑适用于boost::asio::read案例吗?我认为同样的逻辑应该有效。我正在做的事情有些不对,这就是我需要的建议。

如果可能,还需要其他信息:
如果boost::asio::write&amp; boost::asio::read有一个超时参数。我不会写这个额外的。似乎有很多要求asio家伙在他们的同步读取和放大器中引入超时的请求。写功能。就像this one一样。 asio家伙在不久的将来可以解决这个问题吗?

我正在进行同步boost::asio::write&amp;使用工作线程在同一个套接字上的Linux,这个工作线程非常棒。我所缺少的就是这个超时功能。

环境:
我的代码运行在MacOSX&amp;使用C ++ 14编译器的<body> <div> <span>Information:</span><br><span>aaaaa</span> <div id="printhead"> <p> This is the print page header </p> </div> </div> <div id="docbody" > <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> <p>aaaaaaaaaa</p> </div> <div class="footer"> <p class="left">this is a footer Page </p> <p class="right"></p> </div> </body> 。此问题仅涉及 TCP套接字

1 个答案:

答案 0 :(得分:2)

我编写了以下帮助程序以等待任何异步操作与timeout¹同步:

template<typename AllowTime> void await_operation(AllowTime const& deadline_or_duration) {
    using namespace boost::asio;

    ioservice.reset();
    {
        high_resolution_timer tm(ioservice, deadline_or_duration);
        tm.async_wait([this](error_code ec) { if (ec != error::operation_aborted) socket.cancel(); });
        ioservice.run_one();
    }
    ioservice.run();
}

我之后也在TCP客户端上演示了完整版:Boost::Asio synchronous client with timeout

该示例包括写入操作,并已经过完全测试。

完整样本:

从原始帖子中获取“更好”的示例(FTP客户端示例显示更实际的使用模式):

<强> Live On Coliru

#ifndef __TCPCLIENT_H__
#define __TCPCLIENT_H__

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>

class TCPClient {
public:
    void        disconnect();
    void        connect(const std::string& address, const std::string& port);
    std::string sendMessage(const std::string& msg);

private:
    using error_code = boost::system::error_code;

    template<typename AllowTime> void await_operation(AllowTime const& deadline_or_duration) {
        using namespace boost::asio;

        ioservice.reset();
        {
            high_resolution_timer tm(ioservice, deadline_or_duration);
            tm.async_wait([this](error_code ec) { if (ec != error::operation_aborted) socket.cancel(); });
            ioservice.run_one();
        }
        ioservice.run();
    }

    struct raise {
        template <typename... A> void operator()(error_code ec, A...) const {
            if (ec) throw std::runtime_error(ec.message()); 
        }
    };

    boost::asio::io_service      ioservice { };
    boost::asio::ip::tcp::socket socket { ioservice };
};

inline void TCPClient::disconnect() {
    using namespace boost::asio;

    if (socket.is_open()) {
        try {
            socket.shutdown(ip::tcp::socket::shutdown_both);
            socket.close();
        }
        catch (const boost::system::system_error& e) {
            // ignore
            std::cerr << "ignored error " << e.what() << std::endl;
        }
    }
}

inline void TCPClient::connect(const std::string& address, const std::string& port) {
    using namespace boost::asio;

    async_connect(socket, ip::tcp::resolver(ioservice).resolve({address, port}), raise());

    await_operation(std::chrono::seconds(6));
}

inline std::string TCPClient::sendMessage(const std::string& msg) {
    using namespace boost::asio;

    streambuf response;
    async_read_until(socket, response, '\n', raise());

    await_operation(std::chrono::system_clock::now() + std::chrono::seconds(4));

    return {std::istreambuf_iterator<char>(&response), {}};
}
#endif

#include <iostream>

//#include "TCPClient.hpp"

int main(/*int argc, char* argv[]*/) {
    TCPClient client;
    try {
        client.connect("127.0.0.1", "27015");
        std::cout << "Response: " << client.sendMessage("Hello!") << std::endl;
    }
    catch (const boost::system::system_error& e) {
        std::cerr << e.what() << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
}

¹最初为此答案撰写https://stackoverflow.com/a/33445833/85371