在阻塞套接字上,connect() 返回 "操作现在正在进行中"?

12
我有一个阻塞套接字(至少在下面的代码中是这样的):
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sock < 0) {
            ERROR("%s: error opening socket", __func__);
            return (RESP_ERROR);
    }

    t.tv_sec = timeout;
    t.tv_usec = 0;

    int rf = fcntl(sock, F_GETFD);
    ERROR("fcntl ret=%d, ret & O_NONBLOCK = %d", rf, rf & O_NONBLOCK);

    if ((setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&t, sizeof (t)) < 0)
        || (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&t, sizeof (t)))) {
            strerror_r(errno, err, 254);
            ERROR("%s: error on setsockopt -> %s", __func__, err);
            close(sock);
            return (RESP_ERROR);
    }

    rf = fcntl(sock, F_GETFD);
    ERROR("after select fcntl ret=%d, ret & O_NONBLOCK = %d", rf, rf & O_NONBLOCK);

    if (connect(sock, (struct sockaddr *)&dst, sizeof (dst)) != 0) {
            strerror_r(errno, err, 254);
            ERROR("%s: error on connect -> %s", __func__, err);
            close(sock);
            return (RESP_ERROR);
    }

这是来自日志的内容:
Mar 6 10:42:04 tcpclient: fcntl ret=0, ret & O_NONBLOCK = 0
Mar 6 10:42:04 tcpclient: after select fcntl ret=0, ret & O_NONBLOCK = 0
Mar 6 10:42:14 tcpclient: authenticate: error on connect -> Operation now in progress
看起来这是一个阻塞套接字,但返回了非阻塞套接字的典型错误?Linux版本为2.6.18-308.el5。有什么想法吗?

3
"timeout" 的值是什么? - alk
为了验证我的答案,我想进行一些测试。因此我想知道在传递给 connect() 函数之前如何初始化 dst。你确定它的成员变量 sin_family 被正确设置了吗? - alk
2个回答

9
如果超时时间timeout不为0,则调用connect()将在超时后返回。无论是否建立连接,这都会发生。
从超时到期的那一刻起,connect()的行为就像在非阻塞套接字上调用它一样。
关于此情况(直接来自man connect,忽略下面的“立即”):
引用块:

EINPROGRESS

套接字是非阻塞的,无法立即完成连接。可以使用 select(2)或 poll(2)选择写入套接字以等待完成。在 select(2)表示可写性之后,使用 getsockopt(2)读取 SOL_SOCKET 级别的 SO_ERROR 选项,以确定 connect()是否成功完成(SO_ERROR 为零),还是失败(SO_ERROR 是此处列出的常规错误代码之一,解释了失败的原因)。

/blockquote>
顺便问一下:有人能确认这是标准行为,并且明确说明在哪里吗? man 7 socket声明(由我强调):

引用块:

SO_RCVTIMEO 和 SO_SNDTIMEO

指定收发超时时间,直到报告错误。如果没有传输数据并且已达到超时时间,则返回-1,并将errno设置为EAGAIN或EWOULDBLOCK,就像指定套接字为非阻塞一样。[...]超时时间仅对执行套接字I/O的系统调用(例如,read(2),recvmsg(2),send(2),sendmsg(2))有效;对于 select(2),poll(2),epoll_wait(2)等超时时间无效。

/blockquote>

没有关于connect()的说明,所以我不确定我的答案是否正确。


似乎“从超时到期的那一刻开始,connect() 的行为就像在非阻塞套接字上调用一样。”这是关键部分。关闭(sock)会停止这个操作吗? - Ivan Ostres
2
当然,close()会使套接字失效。但是操作系统可能会在一段时间内保留它的地址:端口。为了在操作系统释放它之前重用地址:端口,您可以指定套接字选项SO_REUSEADDR - alk
1
我不明白为什么设置读取或写入超时会影响connect()。我知道的唯一一种超时连接的方法是在非阻塞模式下使用select()。 - user207421

2

尝试使用if (connect(...) < 0)。你可能根本不会收到错误信息。

NB阻塞模式是默认模式,您不需要设置它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接