如何模拟带有超时的boost :: asio :: write [英] How to simulate boost::asio::write with a timeout

查看:106
本文介绍了如何模拟带有超时的boost :: asio :: write的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试模拟超时的boost::asio::write.或者您可以说,我正在尝试将boost::asio::async_write与超时一起使用.

I am trying to simulate boost::asio::write with timeout. Or you can say, I am trying to use boost::asio::async_write with a timeout.

如我所见,boost::asio::write会阻塞,直到所有数据已被写入和写入为止.阅读.这种功能当然需要超时.

As I see, boost::asio::write blocks until all data has been written & read on the other side. This kind of functionality certainly requires a timeout.

所以,通过阅读这里的简单答案罗伯特·海格纳(Robert Hegner)演示了如何使用 timeout 来执行boost::asio::async_read,我正在尝试通过修改来适应相同的写逻辑:

So, Reading through this simple answer here by Robert Hegner which demostrates how to do a boost::asio::async_read with timeout, I am trying adapt the same logic for write by doing so:

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()之后被挂起.

Problem:
The logic works great for read but does not seem to work for write case. Reason is that while (the_socket->get_io_service().run_one()) gets hung after calling the_socket->cancel() twice.

,在阅读情况下,the_socket->cancel()也被称为两次& ;;不会挂在while&的第3个循环上返回.因此,阅读没有问题.

However, in the read case also the_socket->cancel() is called twice & does not hang on the 3rd loop over of the while & returns out. Hence no issues with read.

问题:
我的理解是否错误,因为相同的超时逻辑适用于boost::asio::async_write情况?我认为同样的逻辑应该起作用.我正在做某事,这是我需要提出的建议.

Question:
Is my understanding wrong that same timeout logic would work for boost::asio::async_write case? I think this same logic should work. There is something wrong I am doing which is what I need a suggestion on.

可能的话,还需要其他信息:
如果boost::asio::read& boost::asio::write具有超时参数.我不会写多余的东西.似乎已经有很多要求asio的人在他们的同步读取&中引入超时的要求.写函数.就像这里的这个一样. 是否有asio专家可以在不久的将来解决此请求?

Additional info required if possible:
if boost::asio::read & boost::asio::write had a timeout parameter. I would not have been writing this extra. Seems like there have been a lot of requests to asio guys to introduce a timeout in their sync read & write functions. Like this one here. Is there any scope for asio guys to address this request in near future?

我正在执行同步boost::asio::read& boost::asio::write在同一套接字上使用工作线程非常出色.我所缺少的只是此超时功能.

I am doing a sync boost::asio::read & boost::asio::write on the same socket using a worker thread for which this works superb. All I am missing is this timeout functionality.

环境:
我的代码在Linux& MacOSX与C ++ 14编译器一起使用.这个问题只涉及 TCP套接字.

Environment:
My code runs on Linux & MacOSX with C++ 14 compiler. This question only concerns TCP sockets.

推荐答案

我编写了以下帮助程序,以与超时同步地同步任何异步操作¹:

I've written the following helper to await any async operation synchronously with a 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同步客户端

I've since also demoed that with a full on TCP client: Boost::Asio synchronous client with timeout

该示例包括写入操作,并且已经过全面测试.

The sample includes write operations and has been completely tested.

以原始文章中的更精细"示例为例(FTP客户端示例显示了更实际的使用模式):

Taking the "nicer" example from the original post (the FTP client example shows more realistic usage patterns):

在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

这篇关于如何模拟带有超时的boost :: asio :: write的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆