在unix域套接字上设置connect()超时

时间:2018-01-12 08:59:04

标签: c sockets timeout

使用alarm()是唯一在unix域套接字上设置connect()超时的吗?我已经尝试过select(),它被描述为here,但似乎select()每次都会在unix域套接字上立即返回ok  调用getsockopt(SO_ERROR)没有错误,但fd上的send()返回错误Transport endpoint is not connected。我粘贴下面的select()代码。

我认为使用闹钟会遇到这种情况,但似乎它被认为是一种古老的方式。所以我在这里看看是否还有其他解决方案。提前谢谢。

if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
    syslog(LOG_USER|LOG_ERR, "fcntl get failed: %s", strerror(errno));
    close(fd);
    return -1;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
    syslog(LOG_USER|LOG_ERR, "set fd nonblocking failed: %s", strerror(errno));
    close(fd);
    return -1;
}
if(connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
    if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINPROGRESS) {
        close(fd);
        return -1;
    }
    FD_ZERO(&set);
    FD_SET(fd, &set);
    if(select(fd + 1, NULL, &set, NULL, &timeout) <= 0) {
        close(fd);
        return -1;
    }
    /*
    if(connect(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
        close(fd);
        return -1;
    }
    */
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0) {
        syslog(LOG_USER|LOG_ERR, "getsockopt failed: %s", strerror(errno));
        close(fd);
        return -1;
    }
    if(error != 0) {
        syslog(LOG_USER|LOG_ERR, "getsockopt return error: %d", error);
        close(fd);
        return -1;
    }
}
if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) {
    syslog(LOG_USER|LOG_ERR, "set fd blocking failed: %s", strerror(errno));
    close(fd);
    return -1;
}

2 个答案:

答案 0 :(得分:0)

在另一个帖子中的某个地方(我没有为该页面添加书签),我发现connect()仅建立了TCP连接。这仅意味着在另一端,有一个有效的TCP堆栈,但这并不意味着服务器实际上已经进行了accept()-ed!

例如connect()的示例就像呼叫支持中心,自动语音告诉您,您正在排队,但仍然无法交流。 accept()是接听电话的实际话务员。

对于相同问题,我的解决方案是让客户端在继续其他客户端程序之前,等待服务器实际发送一些内容。我可以将其放入选择超时循环中。

listen()有一个参数,可以在开始删除客户端连接尝试之前将多少个连接放入待办事项中。

答案 1 :(得分:0)

您可以在EINPROGRESS之后使用select()或poll(),如connect man page中所述。如果得到EAGAIN或EWOULDBLOCK,则Unix域套接字已用完积压条目,即由服务器通过listen()调用指定的队列长度。 connect()失败。

请注意,在服务器甚至没有接受调用之前,连接的客户端都可以写入Unix域套接字,直到系统缓冲区已满为止。这适用于每个积压缓冲区。之后会发生故障。

失败的connect()在重试之前可能需要新的套接字。如果连接被拒绝(例如服务器未监听),则select()可能也会返回0。这取决于系统和libray。无论如何,在发生EAGAIN错误之后,有必要重试。例如:

    int rtc, so_error, max_retry = 5;
    socklen_t len = sizeof so_error;
    while ((rtc = connect(fd, (struct sockaddr *)&address, sizeof address)) != 0
        && errno == EAGAIN && --max_retry >= 0) {
        sleep(1);
        // new socket?
    }

    if (rtc < 0 && errno != EINPROGRESS) {
        syslog(LOG_USER|LOG_ERR, "connect returned %d: %s", rtc, strerror(errno));
        close(fd);
        return -1;
    }

    if (rtc < 0)
    {
        fd_set set, wset, eset;
        struct timeval timeout;
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        FD_ZERO(&set);
        FD_SET(fd, &set);
        wset = set;
        eset = set;
        if(select(fd + 1, &set, &wset, &eset, &timeout) <= 0) {
            close(fd);
            return -1;
        }
        // [...]
    }