如何使用 boost::asio 和 io_contexts 从超时的套接字中读取?
How to read from a socket with a timeout using boost::asio with io_contexts?
我有一个相当简单的用例。我想使用 boost::asio::read
从升压套接字读取,但读取调用超时。 IE。如果在 5 秒内没有从套接字中读取任何内容,则调用应该 terminate/throw-an-error/whatever。没有超时的代码如下所示:
Json::Value Client::MakeRequest(const std::string &ip_addr, unsigned short port,
const Json::Value &request)
{
boost::asio::io_context io_context;
Json::StreamWriterBuilder writer_;
std::string serialized_req = Json::writeString(writer_, request);
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
try {
s.connect({boost::asio::ip::address::from_string(ip_addr), port});
} catch(const std::exception &err) {
throw std::runtime_error(err.what());
}
boost::asio::write(s, boost::asio::buffer(serialized_req));
s.shutdown(tcp::socket::shutdown_send);
error_code ec;
char reply[2048];
// I would like to replace this with a call which times out.
size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply),
ec);
...
}
Whosebug 上有示例说明如何使用已弃用的 boost::asio::io_service
来完成此操作。但是,我的代码使用 io_context
代替,因此这是不可行的。此代码来自 an answer by Tom Trebicky in 2017,使用 io_service
:
完成我的任务
template <typename SyncReadStream, typename MutableBufferSequence>
void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_io_service());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });
s.get_io_service().reset();
while (s.get_io_service().run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
我曾尝试将其转换为使用 io_context
,但无济于事。有人可以提供一个使用 io_context
从套接字读取超时的版本吗?
目前,我尝试将其转移到 io_context
是这样的:
template <typename SyncReadStream, typename MutableBufferSequence>
static void ReadWithTimeout(SyncReadStream &s,
const MutableBufferSequence &buffers,
const boost::asio::deadline_timer::duration_type
&expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_executor().context());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const error_code& error)
{
timer_result.reset(error);
});
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers,
[&read_result] (const error_code& error, size_t)
{
read_result.reset(error);
});
s.get_executor().template target<boost::asio::io_context>()->reset();
while (s.get_executor().template target<boost::asio::io_context>()->run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
这会产生以下错误:
====================[ Build | chord_and_dhash | Debug ]=========================
/opt/clion-2021.1.3/bin/cmake/linux/bin/cmake --build /home/patrick/CLionProjects/chord_and_dhash/cmake-build-debug --target chord_and_dhash -- -j 6
[ 8%] Built target gtest
[ 26%] Built target jsoncpp_lib
[ 34%] Built target gtest_main
Scanning dependencies of target chord_and_dhash
[ 39%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/abstract_chord_peer.cpp.o
[ 43%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/chord_peer.cpp.o
[ 47%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/server_test.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/remote_peer.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o
[ 60%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/chord_test.cpp.o
In file included from /usr/include/boost/asio/basic_socket.hpp:22,
from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
from /usr/include/boost/asio.hpp:24,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:15,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:1:
/usr/include/boost/asio/detail/io_object_impl.hpp: In instantiation of ‘boost::asio::detail::io_object_impl<IoObjectService, Executor>::io_object_impl(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; IoObjectService = boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’:
/usr/include/boost/asio/basic_deadline_timer.hpp:182:20: required from ‘boost::asio::basic_deadline_timer<Time, TimeTraits, Executor>::basic_deadline_timer(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; Time = boost::posix_time::ptime; TimeTraits = boost::asio::time_traits<boost::posix_time::ptime>; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:45:37: required from ‘static void Client::ReadWithTimeout(SyncReadStream&, const MutableBufferSequence&, const duration_type&) [with SyncReadStream = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; MutableBufferSequence = boost::asio::mutable_buffers_1; boost::asio::basic_deadline_timer<boost::posix_time::ptime>::duration_type = boost::posix_time::time_duration]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:70:81: required from here
/usr/include/boost/asio/detail/io_object_impl.hpp:61:25: error: ‘class boost::asio::execution_context’ has no member named ‘get_executor’
61 | executor_(context.get_executor())
| ~~~~~~~~^~~~~~~~~~~~
gmake[3]: *** [CMakeFiles/chord_and_dhash.dir/build.make:199: CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o] Error 1
gmake[3]: *** Waiting for unfinished jobs....
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/test/chord_test.cpp:2:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
gmake[2]: *** [CMakeFiles/Makefile2:313: CMakeFiles/chord_and_dhash.dir/all] Error 2
gmake[1]: *** [CMakeFiles/Makefile2:320: CMakeFiles/chord_and_dhash.dir/rule] Error 2
gmake: *** [Makefile:183: chord_and_dhash] Error 2
我能想到的最直接的初始函数是:
json::value MakeRequest(const std::string& ip_addr, uint16_t port,
const json::value& request)
{
boost::asio::io_context io;
tcp::socket s(io);
// connect, send
s.connect({boost::asio::ip::address::from_string(ip_addr), port});
boost::asio::write(s, boost::asio::buffer(serialize(request)));
s.shutdown(tcp::socket::shutdown_send);
// read for max 5s
boost::asio::steady_timer timer(io, 5s);
timer.async_wait([&](error_code ec) { s.cancel(); });
std::string reply_buf;
error_code reply_ec;
async_read(s, boost::asio::dynamic_buffer(reply_buf, 2048),
[&](error_code ec, size_t) { timer.cancel(); reply_ec = ec; });
io.run();
if (!reply_ec || reply_ec == boost::asio::error::eof) {
return json::parse(reply_buf);
} else {
throw boost::system::system_error(reply_ec);
}
}
我有一个相当简单的用例。我想使用 boost::asio::read
从升压套接字读取,但读取调用超时。 IE。如果在 5 秒内没有从套接字中读取任何内容,则调用应该 terminate/throw-an-error/whatever。没有超时的代码如下所示:
Json::Value Client::MakeRequest(const std::string &ip_addr, unsigned short port,
const Json::Value &request)
{
boost::asio::io_context io_context;
Json::StreamWriterBuilder writer_;
std::string serialized_req = Json::writeString(writer_, request);
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
try {
s.connect({boost::asio::ip::address::from_string(ip_addr), port});
} catch(const std::exception &err) {
throw std::runtime_error(err.what());
}
boost::asio::write(s, boost::asio::buffer(serialized_req));
s.shutdown(tcp::socket::shutdown_send);
error_code ec;
char reply[2048];
// I would like to replace this with a call which times out.
size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply),
ec);
...
}
Whosebug 上有示例说明如何使用已弃用的 boost::asio::io_service
来完成此操作。但是,我的代码使用 io_context
代替,因此这是不可行的。此代码来自 an answer by Tom Trebicky in 2017,使用 io_service
:
template <typename SyncReadStream, typename MutableBufferSequence>
void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_io_service());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });
s.get_io_service().reset();
while (s.get_io_service().run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
我曾尝试将其转换为使用 io_context
,但无济于事。有人可以提供一个使用 io_context
从套接字读取超时的版本吗?
目前,我尝试将其转移到 io_context
是这样的:
template <typename SyncReadStream, typename MutableBufferSequence>
static void ReadWithTimeout(SyncReadStream &s,
const MutableBufferSequence &buffers,
const boost::asio::deadline_timer::duration_type
&expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_executor().context());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const error_code& error)
{
timer_result.reset(error);
});
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers,
[&read_result] (const error_code& error, size_t)
{
read_result.reset(error);
});
s.get_executor().template target<boost::asio::io_context>()->reset();
while (s.get_executor().template target<boost::asio::io_context>()->run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
这会产生以下错误:
====================[ Build | chord_and_dhash | Debug ]=========================
/opt/clion-2021.1.3/bin/cmake/linux/bin/cmake --build /home/patrick/CLionProjects/chord_and_dhash/cmake-build-debug --target chord_and_dhash -- -j 6
[ 8%] Built target gtest
[ 26%] Built target jsoncpp_lib
[ 34%] Built target gtest_main
Scanning dependencies of target chord_and_dhash
[ 39%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/abstract_chord_peer.cpp.o
[ 43%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/chord_peer.cpp.o
[ 47%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/server_test.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/remote_peer.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o
[ 60%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/chord_test.cpp.o
In file included from /usr/include/boost/asio/basic_socket.hpp:22,
from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
from /usr/include/boost/asio.hpp:24,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:15,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:1:
/usr/include/boost/asio/detail/io_object_impl.hpp: In instantiation of ‘boost::asio::detail::io_object_impl<IoObjectService, Executor>::io_object_impl(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; IoObjectService = boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’:
/usr/include/boost/asio/basic_deadline_timer.hpp:182:20: required from ‘boost::asio::basic_deadline_timer<Time, TimeTraits, Executor>::basic_deadline_timer(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; Time = boost::posix_time::ptime; TimeTraits = boost::asio::time_traits<boost::posix_time::ptime>; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:45:37: required from ‘static void Client::ReadWithTimeout(SyncReadStream&, const MutableBufferSequence&, const duration_type&) [with SyncReadStream = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; MutableBufferSequence = boost::asio::mutable_buffers_1; boost::asio::basic_deadline_timer<boost::posix_time::ptime>::duration_type = boost::posix_time::time_duration]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:70:81: required from here
/usr/include/boost/asio/detail/io_object_impl.hpp:61:25: error: ‘class boost::asio::execution_context’ has no member named ‘get_executor’
61 | executor_(context.get_executor())
| ~~~~~~~~^~~~~~~~~~~~
gmake[3]: *** [CMakeFiles/chord_and_dhash.dir/build.make:199: CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o] Error 1
gmake[3]: *** Waiting for unfinished jobs....
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/test/chord_test.cpp:2:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
gmake[2]: *** [CMakeFiles/Makefile2:313: CMakeFiles/chord_and_dhash.dir/all] Error 2
gmake[1]: *** [CMakeFiles/Makefile2:320: CMakeFiles/chord_and_dhash.dir/rule] Error 2
gmake: *** [Makefile:183: chord_and_dhash] Error 2
我能想到的最直接的初始函数是:
json::value MakeRequest(const std::string& ip_addr, uint16_t port,
const json::value& request)
{
boost::asio::io_context io;
tcp::socket s(io);
// connect, send
s.connect({boost::asio::ip::address::from_string(ip_addr), port});
boost::asio::write(s, boost::asio::buffer(serialize(request)));
s.shutdown(tcp::socket::shutdown_send);
// read for max 5s
boost::asio::steady_timer timer(io, 5s);
timer.async_wait([&](error_code ec) { s.cancel(); });
std::string reply_buf;
error_code reply_ec;
async_read(s, boost::asio::dynamic_buffer(reply_buf, 2048),
[&](error_code ec, size_t) { timer.cancel(); reply_ec = ec; });
io.run();
if (!reply_ec || reply_ec == boost::asio::error::eof) {
return json::parse(reply_buf);
} else {
throw boost::system::system_error(reply_ec);
}
}