有没有办法取消待处理的操作(没有断开连接)或为boost库函数设置超时?
即。我想在boost asio中阻塞套接字设置超时?
socket.read_some(boost :: asio :: buffer(pData,maxSize),error _);
示例:我想从套接字中读取一些内容,但是如果已经过了10秒,我想抛出错误。
答案 0 :(得分:16)
当问到这个问题时,我猜ASIO没有任何关于如何完成所需OP的例子,即超时阻塞操作,例如阻塞套接字操作。现在有一些示例可以向您展示如何执行此操作。这个例子似乎很长,但那是因为它得到了很好的评论。它展示了如何在“一次性”模式中使用ioservice。
我认为这个例子是一个很好的解决方案。这里的其他解决方案打破了可移植性,并没有利用ioservice。如果可移植性不重要且ioservice似乎要花费很多--THEN--你不应该使用ASIO。无论如何,你都会创建一个ioservice(几乎所有的ASIO功能都依赖于它,甚至同步套接字),所以,利用它。
Timeout a blocking asio tcp operation
Timeout a blocking asio udp operation
ASIO文档已经更新,因此请查看有关如何克服ASIO使用的“陷阱”的新示例。
答案 1 :(得分:9)
你可以做一个async_read并为你想要的时间设置一个计时器。然后,如果计时器触发,则在套接字对象上调用cancel。否则,如果您的阅读发生,您可以取消您的计时器。这要求您使用io_service对象。
编辑:为您找到执行此操作的代码段
答案 2 :(得分:8)
在Linux / BSD下,操作系统直接支持套接字上的I / O操作超时。该选项可以通过setsocktopt()
启用。我不知道boost::asio
是否提供了设置它的方法或公开套接字scriptor以允许您直接设置它 - 后一种情况不是真正可移植的。
为了完整起见,这里是手册页中的描述:
SO_RCVTIMEO 和 SO_SNDTIMEO
Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval. If an input or output function blocks for this period of time, and data has been sent or received, the return value of that function will be the amount of data transferred; if no data has been transferred and the timeout has been reached then -1 is returned with errno set to EAGAIN or EWOULDBLOCK just as if the socket was specified to be non-blocking. If the timeout is set to zero (the default) then the operation will never timeout. Timeouts only have effect for system calls that perform socket I/O (e.g., read(2), recvmsg(2), send(2), sendmsg(2)); timeouts have no effect for select(2), poll(2), epoll_wait(2), etc.
答案 3 :(得分:8)
我有同样的问题,经过一些研究,我能想到的最简单,最干净的解决方案是获取底层本机套接字,然后执行选择直到有数据要读取。选择将采用超时参数。当然,使用本机套接字开始违背首先使用asio的要点,但同样,这似乎是最干净的方式。据我所知,asio没有提供一种方法来轻松实现同步使用。代码:
// socket here is: boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr
// Set up a timed select call, so we can handle timeout cases.
fd_set fileDescriptorSet;
struct timeval timeStruct;
// set the timeout to 30 seconds
timeStruct.tv_sec = 30;
timeStruct.tv_usec = 0;
FD_ZERO(&fileDescriptorSet);
// We'll need to get the underlying native socket for this select call, in order
// to add a simple timeout on the read:
int nativeSocket = a_socket_ptr->native();
FD_SET(nativeSocket,&fileDescriptorSet);
select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);
if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout
std::string sMsg("TIMEOUT on read client data. Client IP: ");
sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());
throw MyException(sMsg);
}
// now we know there's something to read, so read
boost::system::error_code error;
size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);
...
也许这对你的情况很有用。
答案 4 :(得分:4)
TL; DR
socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });
完整答案 多年来,这个问题不断被问到。到目前为止,我看到的答案很差。我将在此问题的第一次出现时在此处添加此信息。
如果作者只想向所有同步和异步io函数添加可选参数超时,那么每个尝试使用ASIO简化其网络代码的人都会非常满意。不幸的是,这不太可能发生(以我的拙见,仅出于意识形态原因,毕竟ASIO中的AS是有原因的。)
因此,到目前为止,这些方法都是为这只可怜的猫做皮的方法,但没有一种方法能令人胃口大开。假设我们需要200毫秒的超时时间。
1)好的(坏的)旧套接字API:
const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops
请注意以下特性: -超时的const int-在Windows上,所需的类型实际上是DWORD,但是幸运的是,当前的编译器集具有相同的类型,因此const int在Win和Posix世界中均可使用。 -(const char *)表示值。在Windows上,需要const char *,而Posix则需要const void *,在C ++中,const char *将无声地转换为const void *,而反之则不成立。
优点:可以使用,并且可能会一直有效,因为套接字API既旧又稳定。很简单。快速。 缺点:从技术上讲,可能需要为setsockopt和宏使用适当的头文件(与Win甚至不同的UNIX风格不同),但是ASIO的当前实现反正会污染全局名称空间。需要一个超时变量。不是类型安全的。在Windows上,要求套接字在重叠模式下才能工作(幸运的是,当前的ASIO实现使用此套接字,但仍然是实现细节)。难看!
2)自定义ASIO套接字选项:
typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });
优点:足够简单。快速。漂亮(使用typedef)。 缺点:取决于ASIO实施细节,该细节可能会发生变化(但是OTOH最终都会发生变化,与受标准化的公共API相比,这种细节不太可能发生变化)。但是,如果发生这种情况,您将不得不根据https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html编写一个类(由于对ASIO的这一部分进行了明显的过度设计,这当然是主要的PITA),或者更好地恢复为1。
3)使用C ++异步/未来功能。
#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
.wait_for(std::chrono::milliseconds{ 200 });
switch (status)
{
case std::future_status::deferred:
//... should never happen with std::launch::async
break;
case std::future_status::ready:
//...
break;
case std::future_status::timeout:
//...
break;
}
优点:标准。 缺点:(实际上)总是启动一个相对较慢的新线程(对于客户端来说可能足够好,但是由于线程和套接字是“昂贵的”资源,将导致服务器出现DoS漏洞)。不要尝试使用std :: launch :: deferred而不是std :: launch :: async来避免新线程启动,因为wait_for将始终返回future_status :: deferred而不会尝试运行代码。
4)ASIO规定的方法-仅使用异步操作(这实际上不是问题的答案)。
优势:如果不需要短事务的巨大可伸缩性,那么对服务器也足够好。 缺点:比较罗word(所以我什至不包括示例-参见ASIO示例)。需要对异步操作及其完成处理程序使用的所有对象进行非常仔细的生命周期管理,这在实践中要求所有在异步操作中包含和使用此类数据的类都从enable_shared_from_this派生,这需要在堆上分配所有此类类,这意味着(至少对于短期操作而言),因为每个堆alloc / dealloc都会使用内存屏障,因此可伸缩性将在大约16个线程后开始逐渐减弱。
答案 5 :(得分:2)
关于grepsedawk提到的内容。有一些示例显示如何在asio doco中的超时部分下取消一段时间后长时间运行的异步操作。 Boost Asio Examples 。 Async TCP client helped me the most.
Happy Asyncing:)
答案 6 :(得分:1)
即使在最初的问题之后几年,仍然没有令人满意的答案。
手动使用select不是一个好选择
调用io_service.run_one()
也是一个坏主意,因为可能还有其他异步选项需要io_service始终run()
。并且有关阻止tcp客户端的文档很难理解。
所以这是我的解决方案。关键的想法如下:
{
Semaphore r_sem;
boost::system::error_code r_ec;
boost::asio::async_read(s,buffer,
[this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
r_ec=ec_;
r_sem.notify();
});
if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
{
s.cancel();
r_sem.wait();
throw boost::system::system_error(boost::asio::error::try_again);
}
else if(r_ec)
throw boost::system::system_error(r_ec);
}
此处Semaphore
只是一个互斥锁和一个condition_variable
wait_for
由http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for
完整代码位于https://github.com/scinart/cpplib/blob/master/include/asio.hpp
示例在https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp 中
更好的示例https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp
答案 7 :(得分:0)
SO_RCVTIMEO
和SO_SNDTIMEO
从timeval
接受"sys/time.h"
结构,而不是int
。因此,@ Pavel Verevkin的选项1将需要使用timeval
而不是int
,并且选项2将需要实现一个类,因为boost::asio::detail::socket_option::integer
仅存储单个整数值。
答案 8 :(得分:-1)
您可以将同步调用包装到期货中,并等待超时(wait_timeout)完成。
当然不是一种尺寸适合所有尺寸,但适用于例如避免慢速连接超时。
答案 9 :(得分:-2)
在* nix上,您将使用alarm(),因此您的套接字调用将失败并显示EINTR