c-select()在非阻塞手动超时connect()调用的繁重条件下失败



我必须实现一个应用程序健康检查机制,我在select中使用了非阻塞套接字,尽管我成功地实现了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
int connect_tout(char * hostname1, int port, int timeoutval)
{
    char *hostname = hostname1;         /* pointer to name of server */
    struct sockaddr_in saddr;               /* socket address */
    int s, i;
    fd_set fd_r, fd_w;
    struct timeval timeout;
    int flags;
    timeout.tv_sec = timeoutval;
    timeout.tv_usec =  0;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = inet_addr(hostname1);
    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    /* set the socket fd to non-blocking mode */
    fcntl(s, F_SETFL, (flags = fcntl(s, F_GETFL)) | O_NONBLOCK);
    connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
    FD_ZERO(&fd_r);
    FD_ZERO(&fd_w);
    FD_SET(s, &fd_r);
    FD_SET(s, &fd_w);
    /*  timeout durring connect() ??  */
    select(s+1, &fd_r, &fd_w, NULL, &timeout);
    if(FD_ISSET(s, &fd_w))
    {
        printf("ALIVEn");
    }
    else
    {
        printf("Conect TIMEOUT n");
        close(s);
        return errno;
    }
    i = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
    if(i)
    {
        printf("Conect failed errno:%dn",errno);
        perror("connect:");
        close(s);
        return errno;
    }
    else
    {
        printf("Connect  passed  and OK n");
        close(s);
        return 1;
    }
    close(s);
    return 1;
}
int main (int argc, char *argv[])
{
    int ret;
    if (argc < 3)
    {
        printf("Usage: %s [host] [port] [timout]n", argv[0]);
        exit(1);
    }
    char *hostname = argv[1];         /* pointer to name of server <IP address>*/
    connect_tout(hostname, atoi(argv[2]), atoi(argv[3])); 
    return 0;
}

但是当我的代码运行机器在非常高的fd使用率下时,我的问题就来了。注意:在我的系统中一次打开多个fd是常见的行为。那么每次都会失败

if(FD_ISSET(s, &fd_w))
{
    printf("ALIVEn");
} 
else
{
    close(s);
    return errno;
    printf("Conect TIMEOUTn");
} 

正如我所说,在这样的环境中,它通过说TIMEOUT而失败,我想知道为什么select没有这么快确定准备好的描述符而失败,这是每一次。FD_ISSET()是否也可能受到质疑
PS:当系统处于正常数量的fds时,它运行良好。很抱歉程序不好,我刚刚把我的示例工作代码粘贴到这里。稍后我将检查是否存在错误

对于非阻塞connect()使用,在收到可写通知后不会再次调用它。相反,您应该使用SO_ERROR选项检查带有getsockopt()的套接字的错误状态。

您没有检查任何调用的返回值,这使得您的代码无法正确确定任何失败。请注意,如果传入的超时本身为0,则不检查该情况,这将导致select()立即返回套接字的即时状态。请注意,套接字API没有记录检查连接套接字的可读通知。

int s = socket(PF_INET, SOCK_STREAM, 0);
assert(!(s < 0));
int r = fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
assert(r == 0);
r = connect(s, (struct sockaddr *)&saddr, sizeof(saddr));
if (r < 0) {
    if (errno == EINPROGRESS) {
        FD_ZERO(&fd_w);
        FD_SET(s, &fd_w);
        r = select(s+1, NULL, &fd_w, NULL, NULL);
        if (r < 0) {
            perror("select");
            abort();
        }
        assert(r == 1);
        assert(FD_ISSET(s, &fd_w));
        int erropt = -1;
        socklen_t errlen = sizeof(erropt);
        r = getsockopt(s, SOL_SOCKET, SO_ERROR, &erropt, &errlen);
        assert(r == 0);
        if (erropt != 0) {
            errno = erropt;
            perror("connect[after select]");
            abort();
        }
        /* connect succeeded asynchronously */
    } else {
        perror("connect[direct call]");
        abort();
    }
} else {
    /* connect succeeded synchronously */
}

最新更新