SO_RCVTIME 和 SO_RCVTIMEO 不影响 Boost.Asio 操作 [英] SO_RCVTIME and SO_RCVTIMEO not affecting Boost.Asio operations

查看:30
本文介绍了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 启动?谢谢!

When I use Telnet to connect, but not sending data, it does not disconnect from a Telnet timeout.Will need to do to make setsockopt kick in? Thanks!

我已将 SO_RCVTIMEO 修改为 SO_SNDTIMEO.仍然无法在指定时间内超时

I have modified SO_RCVTIMEO to SO_SNDTIMEO. Still unable to timeout in the specified time

推荐答案

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

Using SO_RCVTIMEO and SO_SNDTIMEO socket options with Boost.Asio will rarely produce the desired behavior. Consider using either of the following two patterns:

可以通过使用 Boost.Asio 计时器和带有 async_receive() 操作的 async_wait() 操作来组合一个带超时的异步读取操作.这种方法在 Boost.Asio 超时示例,类似于:

One can compose an asynchronous read operation with timeout by using a Boost.Asio timer and an async_wait() operation with a async_receive() operation. This approach is demonstrated in the Boost.Asio timeout examples, something similar to:

// 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()).
  });

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

Be aware that it is possible for both asynchronous operations to complete in the same iteration, making both completion handlers ready to run with success. Hence, the reason why both completion handlers need to update and check state. See this answer for more details on how to manage state.

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

Boost.Asio's provides support for C++11 futures. When boost::asio::use_future is provided as the completion handler to an asynchronous operation, the initiating function will return a std::future that will be fulfilled once the operation completes. As std::future supports timed waits, one can leverage it for timing out an operation. Do note that as the calling thread will be blocked waiting for the future, at least one other thread must be processing the io_service to allow the async_receive() operation to progress and fulfill the promise:

// 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 设置为 EAGAINEWOULDBLOCK.


Why SO_RCVTIMEO Will Not Work

System Behavior

The SO_RCVTIMEO documentation notes that the option only affects system calls that perform socket I/O, such as read() and recvmsg(). It does not affect event demultiplexers, such as select() and poll(), that only watch the file descriptors to determine when I/O can occur without blocking. Furthermore, when a timeout does occur, the I/O call fails returning -1 and sets errno to EAGAIN or EWOULDBLOCK.

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

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 的系统调用将立即返回 EAGAINEWOULDBLOCK.对于非阻塞套接字,SO_RCVTIMEO 不会有任何影响,因为调用将立即返回成功或失败.因此,要使 SO_RCVTIMEO 影响系统 I/O 调用,套接字必须是阻塞的.

When the underlying file descriptor is set to non-blocking, system calls performing socket I/O will return immediately with EAGAIN or EWOULDBLOCK if resources are not immediately available. For a non-blocking socket, SO_RCVTIMEO will not have any affect, as the call will return immediately with success or failure. Thus, for SO_RCVTIMEO to affect system I/O calls, the socket must be blocking.

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

First, asynchronous I/O operations in Boost.Asio will use an event demultiplexer, such as select() or poll(). Hence, SO_RCVTIMEO will not affect asynchronous operations.

接下来,Boost.Asio的sockets有两种非阻塞模式的概念(都默认为false):

Next, Boost.Asio's sockets have the concept of two non-blocking modes (both of which default to false):

  • native_non_blocking() 模式,大致对应文件描述符的非阻塞状态.此模式会影响系统 I/O 调用.例如,如果调用 socket.native_non_blocking(true),则 recv(socket.native_handle(), ...) 可能会失败并返回 errno 设置为 EAGAINEWOULDBLOCK.每当在套接字上启动异步操作时,Boost.Asio 都会启用此模式.
  • non_blocking() 模式,影响 Boost.Asio 的同步套接字操作.当设置为 true 时,Boost.Asio 会将底层文件描述符设置为非阻塞和同步 Boost.Asio 套接字操作可能会失败,boost::asio::error::would_block(或等效的系统错误).当设置为 false 时,Boost.Asio 将阻塞,即使底层文件描述符是非阻塞的,通过轮询文件描述符并重新尝试系统 I/O 操作,如果 EAGAINEWOULDBLOCK 被返回.
  • native_non_blocking() mode that roughly corresponds to the file descriptor's non-blocking state. This mode affects system I/O calls. For example, if one invokes socket.native_non_blocking(true), then recv(socket.native_handle(), ...) may fail with errno set to EAGAIN or EWOULDBLOCK. Anytime an asynchronous operation is initiated on a socket, Boost.Asio will enable this mode.
  • non_blocking() mode that affects Boost.Asio's synchronous socket operations. When set to true, Boost.Asio will set the underlying file descriptor to be non-blocking and synchronous Boost.Asio socket operations can fail with boost::asio::error::would_block (or the equivalent system error). When set to false, Boost.Asio will block, even if the underlying file descriptor is non-blocking, by polling the file descriptor and re-attempting system I/O operations if EAGAIN or EWOULDBLOCK are returned.

non_blocking() 的行为阻止 SO_RCVTIMEO 产生所需的行为.假设 socket.receive() 被调用并且数据既不可用也不接收:

The behavior of non_blocking() prevents SO_RCVTIMEO from producing desired behavior. Assuming socket.receive() is invoked and data is neither available nor received:

  • 如果 non_blocking() 为 false,系统 I/O 调用将根据 SO_RCVTIMEO 超时.但是,Boost.Asio 将立即阻止对文件描述符的轮询以使其可读,这不受 SO_RCVTIMEO 的影响.最终结果是调用方在 socket.receive() 中被阻塞,直到接收到数据或失败,例如远程对等方关闭连接.
  • 如果 non_blocking() 为真,那么底层文件描述符也是非阻塞的.因此,系统I/O调用将忽略SO_RCVTIMEO,立即返回EAGAINEWOULDBLOCK,导致socket.receive() 失败,boost::asio::error::would_block.
  • If non_blocking() is false, the system I/O call will timeout per SO_RCVTIMEO. However, Boost.Asio will then immediately block polling on the file descriptor to be readable, which is not affected by SO_RCVTIMEO. The final result is the caller blocked in socket.receive() until either data has been received or failure, such as the remote peer closing the connection.
  • If non_blocking() is true, then the underlying file descriptor is also non-blocking. Hence, the system I/O call will ignore SO_RCVTIMEO, immediately return with EAGAIN or EWOULDBLOCK, causing socket.receive() to fail with boost::asio::error::would_block.

理想情况下,要使 SO_RCVTIMEO 与 Boost.Asio 一起工作,需要将 native_non_blocking() 设置为 false 以便 SO_RCVTIMEO 可以生效,但也将 non_blocking() 设置为 true 以防止对描述符进行轮询.但是,Boost.Asio 没有 支持这个:

Ideally, for SO_RCVTIMEO to function with Boost.Asio, one needs native_non_blocking() set to false so that SO_RCVTIMEO can take affect, but also have non_blocking() set to true to prevent polling on the descriptor. However, Boost.Asio does not support this:

socket::native_non_blocking(bool 模式)

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

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.

这篇关于SO_RCVTIME 和 SO_RCVTIMEO 不影响 Boost.Asio 操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆