当前位置:  开发笔记 > 编程语言 > 正文

如何在boost asio中阻止套接字设置超时?

如何解决《如何在boostasio中阻止套接字设置超时?》经验,为你挑选了5个好方法。

有没有办法取消挂起操作(没有断开连接)或设置升压库函数的超时?

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

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

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



1> William Symi..:

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

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

超时阻止asio tcp操作

超时阻止asio udp操作

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


这些例子很长,因为文档是3倍冗余的.我不认为"WELL评论".

2> 小智..:

TL; DR

socket.set_option(boost::asio::detail::socket_option::integer{ 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 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的这一部分的明显过度设计,或者更好的是恢复为1。

3)使用C ++异步/未来功能。

#include 
#include 
//...
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个线程后开始逐渐减少。


我得到一个无效的参数(使用c ++ 98提升1.55):`int recvTimeoutInMS = 1000; const boost :: asio :: detail :: socket_option :: integer <SOL_SOCKET,SO_RCVTIMEO> option = boost :: asio :: detail :: socket_option :: integer <SOL_SOCKET,SO_RCVTIMEO>(recvTimeoutInMS); socket_.set_option(option);`

3> grepsedawk..:

您可以执行async_read并为期望的时间设置计时器.然后,如果计时器触发,则在套接字对象上调用cancel.否则,如果您的阅读发生,您可以取消您的计时器.这要求您使用io_service对象.

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

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



4> Nicola Bonel..:

在Linux/BSD下,操作系统直接支持套接字上的I/O操作超时.该选项可以通过启用setsocktopt().我不知道是否boost::asio提供了一种设置方法或暴露套接字scriptor以允许您直接设置它 - 后一种情况不是真正可移植的.

为了完整起见,这里是手册页的描述:

SO_RCVTIMEOSO_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的read_some()仍会在内部继续循环,如果它什么都不读,从而取消了你可能在本机套接字上设置的SO_RCVTIMEO的影响.看起来像使用带有socket.native()的select()仍然是最明显的超时方式.

5> 小智..:

我有同样的问题,经过一些研究,我能想到的最简单,最干净的解决方案是获取底层的本机套接字,并进行选择直到有数据要读取.选择将采用超时参数.当然,使用本机套接字开始违背首先使用asio的要点,但同样,这似乎是最干净的方式.据我所知,asio没有提供一种方法来轻松实现同步使用.码:

        // socket here is:  boost::shared_ptr 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);

        ...

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

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有