【问题标题】:Can an accept socket have a transient failure that's worth retrying?接受套接字是否会出现值得重试的瞬时故障?
【发布时间】:2020-05-12 19:57:51
【问题描述】:

这个问题主要是针对 boost::asio 的,但是socket 标签上的问题可能会对有关accept 调用的暂时性故障有所了解。

在 Boost::Asio 中,如果我有一个套接字接受器编码为持续接受新连接。

void Acceptor::StartNextAccept()
{
    // _acceptor is of type boost::asio::ip::tcp::acceptor

    _acceptor->async_accept([this](const boost::system::error_code& ec, boost::asio::ip::tcp::socket sock) {
        if (ec)
        {
            // error
            LogErrorCode(ec);
        }
        else
        {
            // success
            HandleNewConnection(s);
        }

        StartNextAccept(); // enqueue another accept call regardless of success or error case

    });
}

我担心的是,如果接受器套接字进入错误状态,上述代码将处于无限循环中,不断记录故障,将新尝试排入队列,无限循环。因此,不必要地烧掉内核并填充日志文件。

哪个是更好的假设:

  1. async_accept 调用不应在有效套接字上失败。不用担心上面的代码,因为您在初始化套接字时仔细检查了错误并测试了您的代码。

  2. async_accept 调用可能会失败,但重试它们毫无意义,因此只需关闭此套接字并退出重试循环。

  3. async_accept 调用可能会出现暂时性故障。检查错误 代码来确定是否值得重试。

如果上面的 #3 是正确的假设,建议检查哪些错误代码?如果错误是暂时的(例如机器资源不足、句柄不足等),在重试之前等待几秒钟以使线程不会烧毁内核是否有意义?

更新:物有所值。我的主要平台是 Mac 和 Windows 10。

【问题讨论】:

    标签: sockets boost-asio


    【解决方案1】:

    网络层是否存在值得重试的暂时性问题?是的。

    但是,linux accept 错误是从挂起的连接列表(积压)中返回的,而例如BSD 直接报告它们。

      Error handling
           Linux accept() (and accept4()) passes already-pending network errors
           on the new socket as an error code from accept().  This behavior
           differs from other BSD socket implementations.  For reliable
           operation the application should detect the network errors defined
           for the protocol after accept() and treat them like EAGAIN by
           retrying.  In the case of TCP/IP, these are ENETDOWN, EPROTO,
           ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and
           ENETUNREACH.
    

    不适用于 Asio 的async_connect 的其他条件包括: EWOULDBLOCK/EAGAIN, EFAULT.

    请参阅boost::asio::error 以获取相应的error_code 名称:https://www.boost.org/doc/libs/master/boost/asio/error.hpp

    否则,请查看系统错误列表documented,看看您认为哪些值得明确处理。

    在我的代码中,我通常只是结束链条:

    _acceptor->async_accept([this](const boost::system::error_code& ec, boost::asio::ip::tcp::socket sock) {
        if (ec) {
            LogErrorCode(ec);
        } else {
            HandleNewConnection(s);
            StartNextAccept();
        }
    });
    

    我的服务器将重新初始化监听器(acceptor 用 Asio 说话)。当然,它本身可能会失败,服务器可能会关闭。

    您可能有也可能没有提示您以不同方式处理个别条件的 QoS 要求。

    最终,重新初始化接受器可能会更健壮,例如网络配置何时更改?

    【讨论】:

    • 谢谢。我没有想到客户端在服务器上调用 accept 之前重置他的连接的情况。我的主要平台是 Mac,在较小程度上是 Windows 10。Mac 的手册页列出了一组特定的接受错误,Windows 文档也是如此。因此,我认为这将归结为尽最大努力识别瞬时错误和致命错误,以决定是否将新连接加入队列或重新初始化。
    猜你喜欢
    • 2015-05-24
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 2021-01-29
    • 2020-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多