有没有办法取消待处理的操作(没有断开连接)或为boost库函数设置超时?

即。我想在boost asio中阻塞套接字设置超时?

socket.read_some(boost :: asio :: buffer(pData,maxSize),error _);

示例:我想从套接字中读取一些内容,但是如果已经过了10秒,我想抛出错误。

有帮助吗?

解决方案

在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.

其他提示

当问到这个问题时,我猜ASIO没有任何关于如何完成所需OP的例子,即超时阻塞操作,例如阻塞套接字操作。现在有一些示例可以向您展示如何执行此操作。这个例子似乎很长,但那是因为它得到了很好的评论。它展示了如何在“一次性”模式中使用ioservice。

我认为这个例子是一个很好的解决方案。这里的其他解决方案打破了可移植性,并没有利用ioservice。如果可移植性不重要且ioservice似乎要花费很多--THEN--你不应该使用ASIO。无论如何,你都会创建一个ioservice(几乎所有的ASIO功能都依赖于它,甚至同步套接字),所以,利用它。

超时阻止asio tcp操作

超时阻止asio udp操作

ASIO文档已经更新,因此请查看有关如何克服ASIO使用的“陷阱”的新示例。

你可以做一个async_read并为你想要的时间设置一个计时器。然后,如果计时器触发,则在套接字对象上调用cancel。否则,如果您的阅读发生,您可以取消您的计时器。这要求您使用io_service对象。

编辑:为您找到执行此操作的代码段

http://lists.boost.org/Archives/boost/ 2007/04 / 120339.php

我有同样的问题,经过一些研究,我能想到的最简单,最干净的解决方案是获取底层的本机套接字,并进行选择直到有数据要读取。选择将采用超时参数。当然,使用本机套接字开始违背首先使用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);

        ...

也许这对你的情况很有用。

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 for timeout - 在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 (这当然是一个主要的PITA,这要归功于明显过度工程化这部分ASIO)或更好的还原为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规定的方法 - 仅使用异步操作(这不是问题的答案)。

优点:如果不需要短交易的巨大可扩展性,对服务器也足够好。 缺点:相当冗长(所以我甚至不会包含示例 - 请参阅ASIO示例)。需要对异步操作及其完成处理程序使用的所有对象进行非常仔细的生命周期管理,这实际上要求在异步操作中包含和使用此类数据的所有类都是从enable_shared_from_this派生的,这需要在堆上分配所有这些类,这意味着(至少对于短操作而言,可扩展性将在大约16个线程之后逐渐减少,因为每个堆alloc / dealloc将使用内存屏障。

关于grepsedawk提到的内容。有一些示例显示如何在asio doco中的超时部分下取消一段时间后长时间运行的异步操作。 提升Asio示例异步TCP客户端对我帮助最大。

Happy Asyncing:)

即使在最初的问题之后几年,仍然没有令人满意的答案。

手动使用select不是一个好选择

  1. 文件描述符号必须小于1024
  2. 由于错误的校验和,FD可能被虚假报告为准备就绪。
  3. 调用io_service.run_one()也是一个坏主意,因为可能有其他异步选项需要io_service始终run()。而关于阻止tcp客户端的boost文档很难理解。

    所以这是我的解决方案。关键的想法如下:

    {
        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 /线程/ condition_variable / wait_for

    完整代码位于 https://github.com/scinart /cpplib/blob/master/include/asio.hpp 结果 示例在 https://github.com/scinart/ cpplib /斑点/主/测试/ test_asio.cpp 结果 https://github.com/scinart/cpplib/blob上的更好示例/master/test/test_SyncBoostIO.cpp

您可以将同步调用包装到期货中,并等待超时(wait_timeout)完成。

http:// www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.futures

当然不是一种尺寸适合所有尺寸,但适用于例如避免慢速连接超时。

在* nix上,您将使用alarm(),因此您的套接字调用将失败并显示EINTR

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top