【问题标题】:select() returns 1 on not-connected socket (inconsistent)select() 在未连接的套接字上返回 1(不一致)
【发布时间】:2021-08-19 00:25:54
【问题描述】:

我正在使用非阻塞套接字连接到服务器。
在特定的测试场景中,服务器宕机,这意味着一个 TCP SYN 出去了,但是没有响应,永远无法建立连接。

在此设置中,select 通常会在 2 秒后超时,返回 0。 这是大多数时候的行为,而且看起来是正确的。

但是,在大约 5% 的情况下,select 立即返回 1(表示套接字在掩码中是可读的)。
但是当我从套接字read(2) 时,-1 会返回 'Network is unreachable'

sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// sockfd checked and > 0
// set non-blocking

struct timeval tv{};
tv.tv_sec = 2;

int ret = connect(sockfd, addr, addrlen ); // addr set elsewhere
if (ret < 0 && errno == EINPROGRESS)
{
    fd_set cset;
    FD_ZERO(&cset);
    FD_SET(sockfd, &cset);
    
    ret = select(sockfd + 1, &cset, nullptr, nullptr, &tv);
    // returns 1 sometimes
}

在第一篇文章中,我错误地表示在错误情况下,网络上只有一个 TCP SYN(没有重试)。
这不是真的;在错误和非错误情况下,网络上都有一个 TCP SYN 在 1 秒后重新发送。

什么可能导致这种情况,有没有办法获得与select 一致的行为?

【问题讨论】:

  • 它设置为非阻塞,但我没有将它包含在代码中(虽然有评论)
  • Similar here(也链接在上面的链接中)
  • @ryyker 这个问题是相关的,但情况不同。显然返回了 TCP RST,这使得套接字可读.. 就我而言,网络没有响应
  • 您是否考虑过selecting 用于写作 而不是阅读?这有什么改变吗?

标签: c linux sockets nonblocking


【解决方案1】:

确定非阻塞connect() 是否完成的正确方法是询问select() 可写性而不是可读性。这在connect() documentation中有明确说明:

EINPROGRESS
套接字是非阻塞的,连接不能立即完成。 (UNIX 域套接字以 EAGAIN 代替。)可以通过选择要写入的套接字来完成 select(2)poll(2)。在select(2)表示可写性后,使用getsockopt(2)读取SOL_SOCKET级别的SO_ERROR选项,判断connect()是成功完成(SO_ERROR为零)还是失败(@987654334) @ 是这里列出的常见错误代码之一,解释了失败的原因)。

在您知道实际已首先建立连接之前,使用select()/poll() 测试套接字的可读性是未定义的行为

试试这个:

sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// sockfd checked and > 0
// set non-blocking

int ret = connect(sockfd, addr, addrlen); // addr set elsewhere
if (ret < 0)
{
    if (errno != EINPROGRESS)
    {
        close(sockfd);
        sockfd = -1;
    }
    else
    {
        fd_set cset;
        FD_ZERO(&cset);
        FD_SET(sockfd, &cset);
    
        struct timeval tv{};
        tv.tv_sec = 2;

        ret = select(sockfd + 1, nullptr, &cset, nullptr, &tv);
        if (ret <= 0)
        {
            close(sockfd);
            sockfd = -1;
        }
        else
        {
            int errCode = 0;
            socklen_t len = sizeof(errCode);
            getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &errCode, &len);

            if (errCode != 0)
            {
                close(sockfd);
                sockfd = -1;
            }
        }
    }
}

if (sockfd != -1)
{
    // use sockfd as needed (read(), etc) ...
    close(sockfd);
}

【讨论】:

  • 当然你可以测试可读性,它只会指示可读性(即从对等方接收到的数据)而不是成功建立的连接。
  • 谢谢,我根据您的建议更改了代码。getsockopt 在大约 5% 的连接尝试中仍然返回 network unreachable。奇怪的是,即使有这个错误,我也可以看到网络上的 TCP SYN 出去了。我可以试试ppoll,但如果这是来自内核的消息,它可能会触发相同的行为
  • @curiousguy12 我从来没有说过不会发生“网络无法访问”错误。但是在套接字报告可写性而不是可读性之前,您不能确定错误是否可用。
猜你喜欢
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 2013-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多