【问题标题】:Using select() for non-blocking sockets to connect always returns 1对非阻塞套接字使用 select() 连接总是返回 1
【发布时间】:2012-07-01 22:23:48
【问题描述】:

这个问题与In a non blocking socket connect, select() always returns 1 非常相似(或几乎相同);但是,我似乎找不到我的代码出现问题的地方。

我正在使用非阻塞套接字,并希望在将客户端连接到服务器时使用 select() 来检查超时/成功。问题是 select() 总是几乎立即返回 1,即使我什至没有运行服务器并且没有任何东西可以连接。在此先感谢您的帮助,代码sn-p如下:

//Loop through the addrinfo structs and try to connect to the first one we can
for(p = serverinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) 
    {
        //We couldn't create the socket, try again
        perror("client: socket");
        continue;
    }

    //Set the socket to non-blocking
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

    if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
        //The error was something other than non-block/in progress, try next addrinfo
        if(errno != EINPROGRESS) 
        {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        fd_set write_fds;
        FD_ZERO(&write_fds);            //Zero out the file descriptor set
        FD_SET(sockfd, &write_fds);     //Set the current socket file descriptor into the set

        //We are going to use select to wait for the socket to connect
        struct timeval tv;              //Time value struct declaration
        tv.tv_sec = 5;                  //The second portion of the struct
        tv.tv_usec = 0;                 //The microsecond portion of the struct

        //DEBUG: This is ALWAYS 1
        int select_ret = select(sockfd + 1, NULL, &write_fds, NULL, &tv);
        cout << select_ret << endl;

        //Check return, -1 is error, 0 is timeout
        if(select_ret == -1 || select_ret == 0)
        {
            //We had an error connecting
            cout << "Error Connecting\n";
            close(sockfd);
            continue;
        }
    }

    //We successfully connected, break out of loop
    break;
}

【问题讨论】:

标签: c sockets select nonblocking


【解决方案1】:

期望 select() 返回什么?考虑到 select() 通常用于等待多个文件描述符 - 如果您连接 两个,您如何仅根据 select 的返回值知道哪个成功/失败?显然你不会。

这就是为什么 select() 只是告诉您哪些文件描述符以某种方式发生了变化,并且您应该独立确定那是什么。在 connect() 的情况下,您应该调用 getsockopt() 来检索连接尝试的结果。请参阅this answer,我在其中解释了如何执行非阻塞 connect()。

【讨论】:

  • 这不是它在文档中所说的。你应该再次调用 connect()。
  • EJP 我在文档中看到解释了如何按照 Ambroz 描述的方式进行操作,这是更好的方法吗?
  • @EJP 我很想知道您在谈论什么文档。 Linux connect(2) 手册页中的官方方法是getsockopt(fd, SOL_SOCKET, SO_ERROR, ...) 我知道这是因为我编写了手册页的那一部分。很久以前,我使用“再次连接”方法编写了一个程序。它适用于我,但不适用于 BSD 用户,他们坚持认为第二次连接是愚蠢的仅限 Linux 的事情。
  • 当套接字变为可写时,我们的生产软件使用带有 (SOL_SOCKET, SO_ERROR) 的 getsockopt。而且效果很好。也许 EJP 有另一种工作方式。但这个答案是正确的。
  • select() 返回活动套接字的数量。它并不总是返回 1。这不能回答问题。
【解决方案2】:

当以非阻塞模式连接并且 select() 指示连接是可写的时,您应该再次调用 connect()。这样做将返回 -1 并带有 errno == ECONNRESET 或其他任何值。

【讨论】:

  • 真的吗?您能否参考一些保证这将起作用的东西,而不是开始新的连接尝试?连接状态的 linux 手册页(解释 EINPROGRESS 错误):在 select(2) 指示可写后,使用 getsockopt(2) 读取 SOL_SOCKET 级别的 SO_ERROR 选项以确定 connect() 是否成功完成(SO_ERROR 为零)或不成功(SO_ERROR 是这里列出的常见错误代码之一,解释了失败的原因)。
  • @AmbrozBizjak Stevens and Wright,TCP/IP Illustrated; Stevens,Unix 网络编程;* 和 Douglas Comer 的 TCP 书,名字记错了。
  • 对不起,可能应该评论这个答案而不是上面的答案。我遇到了类似的问题,并认为我可能需要再次调用 connect() ,但是除了这篇文章之外的所有搜索都没有表明这一点。此外,connect 的文档也没有提到这一点。事实上 ECONNRESET 甚至没有被列为可能的 connect() 错误。但是我确实看到了 EISCONN。
猜你喜欢
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
相关资源
最近更新 更多