SO_RCVTIME和SO_RCVTIMEO不影响Boost.Asio操作

以下是我的代码

boost::asio::io_service io; boost::asio::ip::tcp::acceptor::reuse_address option(true); boost::asio::ip::tcp::acceptor accept(io); boost::asio::ip::tcp::resolver resolver(io); boost::asio::ip::tcp::resolver::query query("0.0.0.0", "8080"); boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); accept.open(endpoint.protocol()); accept.set_option(option); accept.bind(endpoint); accept.listen(30); boost::asio::ip::tcp::socket ps(io); accept.accept(ps); struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; //setsockopt(ps.native(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); setsockopt(ps.native(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); char buf[1024]; ps.async_receive(boost::asio::buffer(buf, 1024), boost::bind(fun)); io.run(); 

当我使用Telnet连接但不发送数据时,它不会从Telnet超时断开连接。将需要做什么使setsockopt踢? 谢谢!

我已经将SO_RCVTIMEO修改为SO_SNDTIMEO。 仍然无法在指定的时间超时

Solutions Collecting From Web of "SO_RCVTIME和SO_RCVTIMEO不影响Boost.Asio操作"

对Boost.Asio使用SO_RCVTIMEOSO_SNDTIMEO套接字选项很少会产生所需的行为。 考虑使用以下两种模式之一:

使用async_wait()组合操作

通过使用Boost.Asio定时器和使用async_receive()操作的async_wait()操作,可以组成一个超时的异步读取操作。 在Boost.Asio 超时示例中演示了这种方法,类似于:

 // Start a timeout for the read. boost::asio::deadline_timer timer(io_service); timer.expires_from_now(boost::posix_time::seconds(1)); timer.async_wait( [&socket, &timer](const boost::system::error_code& error) { // On error, such as cancellation, return early. if (error) return; // Timer has expired, but the read operation's completion handler // may have already ran, setting expiration to be in the future. if (timer.expires_at() > boost::asio::deadline_timer::traits_type::now()) { return; } // The read operation's completion handler has not ran. boost::system::error_code ignored_ec; socket.close(ignored_ec); }); // Start the read operation. socket.async_receive(buffer, [&socket, &timer](const boost::system::error_code& error, std::size_t bytes_transferred) { // Update timeout state to indicate the handler has ran. This // will cancel any pending timeouts. timer.expires_at(boost::posix_time::pos_infin); // On error, such as cancellation, return early. if (error) return; // At this point, the read was successful and buffer is populated. // However, if the timeout occurred and its completion handler ran first, // then the socket is closed (!socket.is_open()). }); 

请注意,这两个异步操作都可以在同一个迭代中完成,使得两个完成处理程序都可以成功运行。 因此,完成处理程序需要更新和检查状态的原因。 有关如何管理状态的更多详细信息,请参阅此答案。

使用std::future

Boost.Asio提供对C ++ 11期货的支持 。 当提供boost::asio::use_future作为异步操作的完成处理程序时,启动函数将返回一个std::future ,一旦操作完成,将会执行。 由于std::future支持定时等待,所以可以利用它来超时操作。 请注意,由于调用线程将被阻塞,等待将来,至少有一个其他线程必须处理io_service以允许async_receive()操作进行并履行承诺:

 // Use an asynchronousous operation so that it can be cancelled on timeout. std::future<std::size_t> read_result = socket.async_receive( buffer, boost::asio::use_future); // If timeout occurs, then cancel the read operation. if (read_result.wait_for(std::chrono::seconds(1)) == std::future_status::timeout) { socket.cancel(); } // Otherwise, the operation completed (with success or error). else { // If the operation failed, then read_result.get() will throw a // boost::system::system_error. auto bytes_transferred = read_result.get(); // process buffer } 

为什么SO_RCVTIMEO不起作用

系统行为

SO_RCVTIMEO文档指出,该选项仅影响执行套接字I / O的系统调用,例如read()recvmsg() 。 它不会影响事件解复用器(如select()poll() ,只监视文件描述符以确定I / O何时可以不受阻塞地发生。 而且,当超时发生时,I / O调用失败返回-1 ,并将errnoEAGAINEWOULDBLOCK

指定接收或发送超时,直到报告错误。 如果没有数据传输并且达到超时,则返回-1 ,并将errno设置为EAGAINEWOULDBLOCK […]超时仅对执行套接字I / O的系统调用有效(例如, read()recvmsg() ,[…];超时对select()poll()epoll_wait()等没有影响。

当底层文件描述符设置为非阻塞时,执行套接字I / O的系统调用将立即返回EAGAINEWOULDBLOCK如果资源不是立即可用的话。 对于非阻塞套接字, SO_RCVTIMEO不会有任何影响,因为调用会立即返回成功或失败。 因此,对于SO_RCVTIMEO来影响系统I / O调用,套接字必须被阻塞。

Boost.Asio行为

首先,Boost.Asio中的异步I / O操作将使用事件解复用器,如select()poll() 。 因此, SO_RCVTIMEO不会影响异步操作。

接下来,Boost.Asio的套接字具有两个非阻塞模式的概念(两者默认为false):

  • native_non_blocking()模式,大致对应于文件描述符的非阻塞状态。 该模式影响系统I / O调用。 例如,如果调用socket.native_non_blocking(true) ,则recv(socket.native_handle(), ...)可能会失败,并将errno设置为EAGAINEWOULDBLOCK 。 任何时候在套接字上启动一个异步操作,Boost.Asio将启用这个模式。
  • 影响Boost.Asio的同步套接字操作的non_blocking()模式。 当设置为true ,Boost.Asio将底层文件描述符设置为非阻塞,同步Boost.Asio套接字操作可能会因boost::asio::error::would_block (或等效系统错误)而失败。 当设置为false ,即使底层文件描述符是非阻塞的,Boost.Asio也会阻塞,如果返回EAGAINEWOULDBLOCK则轮询文件描述符并重新尝试系统I / O操作。

non_blocking()的行为阻止了non_blocking()产生期望的行为。 假设socket.receive()被调用,数据既不可用也不被接收:

  • 如果non_blocking()为false,则系统I / O调用将按照non_blocking()超时。 但是,Boost.Asio会立即阻止对文件描述符的轮询,使其可读,而不受SO_RCVTIMEO影响。 最后的结果是socket.receive()被阻塞的调用者,直到数据已被接收或失败,例如远程对等关闭连接。
  • 如果non_blocking()为true,那么底层文件描述符也是非阻塞的。 因此,系统I / O调用将忽略EWOULDBLOCK ,立即返回EAGAINEWOULDBLOCK ,导致socket.receive()失败, boost::asio::error::would_block

理想情况下,为了使native_non_blocking()与Boost.Asio一起工作,需要将native_non_blocking()设置为false,以便non_blocking()可以non_blocking() ,同时还要将non_blocking()设置为true以防止轮询描述符。 但是,Boost.Asio不支持这个 :

socket::native_non_blocking(bool mode)

如果模式为false ,但non_blocking()的当前值为true ,则此函数失败,并且boost::asio::error::invalid_argument ,因为组合没有意义。

由于您正在接收数据,因此您可能需要设置: SO_RCVTIMEO而不是SO_SNDTIMEO

虽然混合提升和系统调用可能不会产生预期的结果。

以供参考:

SO_RCVTIMEO

设置指定输入​​函数等待完成的最长时间的超时值。 它接受一个timeval结构,秒数和微秒数指定了等待输入操作完成的时间限制。 如果接收操作在没有接收到附加数据的情况下被阻塞了这么多时间,如果没有收到数据,它应该返回一个部分计数或错误设置为[EAGAIN][EWOULDBLOCK] 。 此选项的默认值为零,表示接收操作不应超时。 该选项采用timeval结构。 请注意,并非所有实现都允许设置此选项。

然而,这个选项只对读操作有效,而不是在异步实现中可能在套接字上等待的其他低级函数(例如select和epoll),而且它似乎也不会影响异步asio操作。

我发现一个示例代码来自boost,可能适用于你的情况。

一个简单的例子(在C ++ 11中编译):

 #include <boost/asio.hpp> #include <boost/bind.hpp> #include <iostream> void myclose(boost::asio::ip::tcp::socket& ps) { ps.close(); } int main() { boost::asio::io_service io; boost::asio::ip::tcp::acceptor::reuse_address option(true); boost::asio::ip::tcp::acceptor accept(io); boost::asio::ip::tcp::resolver resolver(io); boost::asio::ip::tcp::resolver::query query("0.0.0.0", "8080"); boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); accept.open(endpoint.protocol()); accept.set_option(option); accept.bind(endpoint); accept.listen(30); boost::asio::ip::tcp::socket ps(io); accept.accept(ps); char buf[1024]; boost::asio::deadline_timer timer(io, boost::posix_time::seconds(1)); timer.async_wait(boost::bind(myclose, boost::ref(ps))); ps.async_receive(boost::asio::buffer(buf, 1024), [](const boost::system::error_code& error, std::size_t bytes_transferred ) { std::cout << bytes_transferred << std::endl; }); io.run(); return 0; }