问题描述
我正在尝试模拟 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 & 的第三个循环上返回.因此读取没有问题.
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.
推荐答案
我编写了以下帮助程序来等待 any 异步操作与超时同步:
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):
#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, '
', 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 超时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!