c – 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(),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.仍无法在指定时间内超时

解决方法

在Boost.Asio中使用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 to EAGAIN or EWOULDBLOCK […] Timeouts only have effect for system calls that perform socket I/O (e.g.,read(),recvmsg(),[…]; timeouts have no effect for select(),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 of non_blocking() is true,this function fails with boost::asio::error::invalid_argument,as the combination does not make sense.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...