2008-11-15 46 views
23

有没有办法取消挂起的操作(没有断开连接)或为boost库函数设置超时?如何在boost asio中设置阻塞套接字的超时时间?

I.e.我想在boost asio中设置阻塞套接字的超时时间? socket.read_some(boost :: asio :: buffer(pData,maxSize),error_);

例如:我想从套接字中读取一些信息,但是如果10秒钟过去了,我想抛出一个错误。

回答

8

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

这将是一个很好的解决方案,但他们没有这样的套接字选项。请参阅插槽选项在这里:http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/reference.html – 2008-11-15 14:19:06

+0

提升是好的,但不完美:-) – 2008-11-17 11:04:48

+4

但从我可以告诉,asio的read_some ()将在内部继续循环,如果它什么都不读取,则取消您可能在本机套接字上设置的SO_RCVTIMEO的效果。看起来像使用select()和socket.native()将仍然是最明显的超时方式。 – 2012-04-14 18:18:16

-2

在* nix,你会使用报警(),所以你的套接字调用会失败EINTR

+0

难道所有的套接字调用,在所有的线程将失败并EINTR? 听起来很糟糕。 – 2009-11-04 11:01:12

9

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

编辑:找到一个代码片段为你做这个

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

6

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

     ... 

也许这对你的情况有用。

13

当这个问题被问,我猜ASIO并没有对如何做到需要什么的OP任何例子,那就是超时阻塞操作,如阻塞套接字操作。现在有一些例子可以告诉你如何做到这一点。这个例子似乎很长,但那是因为它很好评论。它显示了如何在“一次性”模式下使用ioservice。

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

Timeout a blocking asio tcp operation

Timeout a blocking asio udp operation

的ASIO文档已更新,所以检查出来就如何克服一些“陷阱” ASIO的使用有新的例子。

1

甚至在原始问题后的几年,仍然没有令人满意的答案。

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

  1. 文件描述符号必须由于错误校验和是小于1024
  2. FD可以被虚假报告为准备就绪。

呼叫io_service.run_one()也是一个糟糕的主意,因为有可能是需要一个io_service对象总是run()其他异步选项。而boost的关于阻塞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_forhttp://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