linux:为什么在接受调用失败时connect()阻塞?

时间:2016-08-03 23:15:38

标签: c linux sockets

我正在客户端UNIX套接字上执行阻塞connect()调用。以下是代码示例:

    // Create socket.

    fds[i] = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fds[i] == -1)
        {
        result = -1;
        goto done;
        }
    printf("generate_load thread, fds[%d]: %d\n", i, fds[i]);
//      int flags = fcntl(fds[i], F_GETFL);
//      fcntl(fds[i], F_SETFL, flags | O_NONBLOCK);

    // If we have a timeout value we're only going to use that as
    // a connect timeout.  From looking at some source code, it
    // appears the only way to timeout (correctly) a unix domain
    // socket connect() call is to set the send timeout.

    struct timeval existing_timeout;
    if (timeout != 0)
        {
        socklen_t len = sizeof(existing_timeout);
        getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &existing_timeout,
                &len);

        struct timeval tv;
        tv.tv_sec = timeout / 1000000;
        tv.tv_usec = timeout % 1000000;
        setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
        }

    // Set socket name.

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);

    // @ indicates abstract name and abstract names begin with a NULL
    // byte.

    if (socket_name[0] == '@')
        addr.sun_path[0] = '\0';

    // Connect.

    result = connect(fds[i], (struct sockaddr*) &addr, sizeof(addr));
    if (result == -1)
        {
        printf("generate_load thread, failed connecting: %d\n", errno);
        if (errno == EAGAIN)
            errno = ETIMEDOUT;
        goto done;
        }

    printf("generate_load thread, connected fds[%d]: %d\n", i, fds[i]);

    // If we set a timeout then set it back to what it was.

    if (timeout != 0)
        {
        setsockopt(fds[i], SOL_SOCKET, SO_SNDTIMEO, &existing_timeout,
                sizeof(existing_timeout));
        }

此代码一切正常,直到接受方(目前处于同一进程)由于文件描述符限制而失败。 accept()调用失败,错误为errno = 24(EMFILE)。我很好地得到错误,但为什么客户端没有看到错误?相反,客户端被阻止,永远不会返回。如您所见,我注释掉了将套接字置于非阻塞模式的行。我相信在非阻塞模式下我会遇到一些EAGAIN错误。

此外,当我达到文件描述符限制时,接受方似乎总是试图接受该套接字。我正在使用select()并等待侦听套接字准备好读取。当它是我做一个接受()。我可以理解得到第一个EMFILE错误,但我认为错误会被传回到connect()调用,这会导致代码突破其循环,因此不会再进行连接调用我原以为会导致接受方在select()调用中被阻止。

以下是听力方面的片段。下面的代码在while(1)循环中,首先调用select():

if (FD_ISSET(ti->listen_fd, &read_set) != 0)
    {
    printf("select thread, accepting socket\n");
    int sock = accept(ti->listen_fd, NULL, NULL);
    printf("select thread, accepted socket\n");
    if (sock == -1)
        {
        printf("select thread, failed accepting socket: %d\n", errno);
        if (error_threshold_met(&eti) == 0)
            {
            log_event(LOG_LEVEL_ERROR, "select thread, accept() "
                    "failed: %s", get_error_string(errno, error_string,
                    sizeof(error_string)));
            }
        }

代码似乎工作正常,直到我达到1024文件描述符限制。任何想法为什么它这样做?它应该是,我只是不明白它应该如何工作?

谢谢, 尼克

1 个答案:

答案 0 :(得分:3)

connect()accept()未互锁。您可以致电connect()并让其返回,而根本不会致电accept()。 TCP握手的服务器端部分在内核中独立于accept()发生。 accept()所做的就是从队列中选择一个传入连接并在其周围创建一个套接字,在队列为空时阻塞。由于FD耗尽,套接字创建部分失败,但实际连接已经建立。