【问题标题】:set connect() timeout on unix domain socket在 unix 域套接字上设置 connect() 超时
【发布时间】:2018-06-21 16:55:27
【问题描述】:

使用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;
}

【问题讨论】:

  • connect() 在 unix 域套接字上将立即完成或失败。但是,如果您愿意,您可以使用 select(),就像使用 select 在 TCP 套接字上执行非阻塞 connect() 一样。如果 select 立即返回 0,您可能做错了什么。
  • @nos 但是我做了一个测试,我通过发送信号 SIGSTOP 暂停域套接字的服务器端,然后用 strace 一次又一次地运行客户端可执行文件,几次后,strace 显示客户端挂在 connect() 系统调用上。
  • 你的文件描述符设置为非阻塞模式了吗?
  • @nos 不,因为我想要超时。如果我将 fd 设置为非阻塞,它根本不会等待。
  • @nos 我已经编辑了我的线程以粘贴我的 select() 代码。代码运行良好,select()返回码ok(大于零),但是send()会返回错误Transport endpoint is not connected

标签: c sockets timeout


【解决方案1】:

在另一篇文章的某个地方(我没有为该页面添加书签)我发现connect() 只建立了一个 TCP 连接。只是表示在另一端有一个工作的 TCP 栈,但不代表服务器实际上有accept()-ed!

connect() 的例子就像打电话给支持中心,自动语音告诉你,你在排队,但你仍然无法沟通。 accept() 是接听您电话的实际接线员。

我对同一问题的解决方案是让客户端等待服务器实际发送某些内容,然后再继续处理其他客户端内容。我可以把它放在一个选择超时循环中。

listen() 有一个参数,在开始丢弃客户端连接尝试之前,可以将多少个连接放入 backlog。

【讨论】:

    【解决方案2】:

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

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

    失败的 connect() 在重试之前可能需要一个新的套接字。如果连接被拒绝,select() 也可能返回 0,例如如果服务器没有 listen()。这取决于系统和库。无论如何,在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;
            }
            // [...]
        }
    
    

    【讨论】:

      猜你喜欢
      • 2012-04-22
      • 2012-12-27
      • 1970-01-01
      • 2016-04-18
      • 1970-01-01
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多