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(),SO_RCVTIMEO,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.仍无法在指定时间内超时
解决方法
使用async_wait()进行组合操作
通过使用Boost.Asio定时器和async_wait()操作以及async_receive()操作,可以使用超时组成异步读取操作.这种方法在Boost.Asio timeout examples中得到了证明,类似于:
// 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,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()). });
请注意,两个异步操作都可以在同一次迭代中完成,使两个完成处理程序都可以成功运行.因此,两个完成处理程序需要更新和检查状态的原因.有关如何管理状态的更多详细信息,请参阅this答案.
使用std :: future
Boost.Asio提供support for C++11 futures.当boost::asio::use_future
作为异步操作的完成处理程序提供时,启动函数将返回一个操作完成后将完成的std :: future.由于std :: future支持定时等待,因此可以利用它来超时操作.请注意,由于调用线程将被阻塞等待将来,至少有一个其他线程必须处理io_service以允许async_receive()操作进行并履行承诺:
// Use an asynchronous 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并将errno设置为EAGAIN或EWOULDBLOCK.
Specify the receiving or sending timeouts until reporting an error. […] if no data has been transferred and the timeout has been reached then
-1
is returned with errno set toEAGAIN
orEWOULDBLOCK
[…] Timeouts only have effect for system calls that perform socket I/O (e.g.,read()
,recvmsg()
,[…]; timeouts have no effect forselect()
,poll()
,epoll_wait()
,and so on.
当基础文件描述符设置为非阻塞时,如果资源不能立即可用,则执行套接字I / O的系统调用将立即返回EAGAIN或EWOULDBLOCK.对于非阻塞套接字,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设置为EAGAIN或EWOULDBLOCK.无论何时在套接字上启动异步操作,Boost.Asio都将启用此模式.
> non_blocking()
模式影响Boost.Asio的同步套接字操作.设置为true时,Boost.Asio将基础文件描述符设置为非阻塞,并且同步Boost.Asio套接字操作可能会因boost :: asio :: error :: would_block(或等效系统错误)而失败.设置为false时,Boost.Asio将阻止,即使基础文件描述符是非阻塞的,也可以通过轮询文件描述符并重新尝试系统I / O操作(如果返回EAGAIN或EWOULDBLOCK).
non_blocking()的行为可防止SO_RCVTIMEO产生所需的行为.假设调用了socket.receive()并且数据既不可用也没有接收:
>如果non_blocking()为false,则系统I / O调用将根据SO_RCVTIMEO超时.但是,Boost.Asio将立即阻止对文件描述符的轮询,使其可读,而不受SO_RCVTIMEO的影响.最终结果是调用者在socket.receive()中被阻塞,直到收到数据或失败,例如远程对等体关闭连接.
>如果non_blocking()为true,则基础文件描述符也是非阻塞的.因此,系统I / O调用将忽略SO_RCVTIMEO,立即返回EAGAIN或EWOULDBLOCK,导致socket.receive()失败并使用boost :: asio :: error :: would_block.
理想情况下,要使SO_RCVTIMEO与Boost.Asio一起工作,需要将native_non_blocking()设置为false以使SO_RCVTIMEO生效,但也将non_blocking()设置为true以防止对描述符进行轮询.但是,Boost.Asio不是support this:
socket::native_non_blocking(bool mode)
If the mode is
false
,but the current value ofnon_blocking()
istrue
,this function fails withboost::asio::error::invalid_argument
,as the combination does not make sense.