【问题标题】:select() returns 0 with an error Operation now in progress after connect()select() 在 connect() 之后返回 0 并出现错误 Operation now in progress
【发布时间】:2022-01-05 20:01:50
【问题描述】:

所以我在这里建立了一个到服务器的 TCP 连接,并在循环中从应用程序调用,有时我最终会看到以下错误

select() timed out after 4 seconds - Operation now in progress

这意味着select 确实返回了0,这意味着它在 5 秒内超时,而没有观察到文件描述符上的任何活动。

我的理解是在connect() 之后设置非阻塞模式,以防它没有立即与getsockopt() 连接,表明connect() 调用是否确实建立但由于某种原因,select 似乎返回 0。它必须与延迟太小?

int InitializeSocket(int sockType, int protocol, long timeout)
{
    int socketFd = socket(AF_INET, sockType, protocol);
    if (socketFd < 0)
    {
        perror ("Failed to create a client socket of type %d", sockType);
        return -1;
    }
    
    if (timeout > 0)
    {
        struct timeval sockTimeout = {.tv_sec = timeout, .tv_usec = 0};

        // setting the receive timeout
        if (setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &sockTimeout, sizeof(sockTimeout)) < 0) 
        {
            perror ("Failed to set the RX timeout");
            return -1;
        }

        // setting the send timeout
        if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, &sockTimeout, sizeof(sockTimeout)) < 0) 
        {
            perror ("Failed to set the TX timeout");
            return -1;
        }
    }
    return socketFd;
}

void OpenTcpConnection(int serverTimeout, int port, const char *ipAddr)
{
    struct sockaddr_in *address
    
    int socketFd = InitializeSocket(SOCK_STREAM, 0, serverTimeout);
    if (socketFd == -1)
    {
        return -1;
    }
    
    address->sin_family = AF_INET;
    address->sin_port = htons(port);
    address->sin_addr.s_addr = inet_addr(ipAddr);
    memset(address->sin_zero, '\0', sizeof(address->sin_zero));
    
    // get the existing file flags
    long arg = 0;
    if( (arg = fcntl(socketFd, F_GETFL, NULL)) < 0) 
    { 
        perror ("Failed to get file status flags"); 
        exit(0); 
    } 

    // set the socket to nonblocking mode
    arg |= O_NONBLOCK; 
    if( fcntl(socketFd, F_SETFL, arg) < 0) 
    { 
        perror ("Failed to set to nonblocking mode");
        return -1;
    } 
    
    // connect to the server
    int res = connect(socketFd, (struct sockaddr *) &address, sizeof(address));

    fd_set fdset;
    struct timeval tv;
    long selectTimeout = 4; // connect() timeout

    if (res < 0) 
    { 
        // the socket is nonblocking & the connection cannot be completed immediately
        if (errno == EINPROGRESS) 
        { 
            do 
            { 
                tv.tv_sec = selectTimeout; 
                tv.tv_usec = 0; 
                FD_ZERO(&fdset); 
                FD_SET(socketFd, &fdset); 
                res = select(socketFd+1, NULL, &fdset, NULL, &tv); 
                
                if (res < 0 && errno != EINTR) 
                { 
                    perror ("Failed to monitor socket FD %d", socketFd);
                    return -1;
                } 
                else if (res > 0) 
                { 
                    int so_error;
                    socklen_t len = sizeof so_error;
                    int valopt; 

                    // check whether connect() completed successfully
                    if (getsockopt(socketFd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len) < 0) 
                    { 
                        perror ("Error in getsockopt"); 
                        return -1;
                    } 
               
                    if (valopt) 
                    { 
                        perror ("Error in delayed connection");
                        return -1;
                    }
                    break;
                } 
                else
                {
                    perror ("select() timed out after %ld seconds", selectTimeout); // ERROR HERE !!!
                    return -1;
                }
            } while(1);
        }
    }
}

【问题讨论】:

  • 请注意,在select() 返回除-1 以外的任何值之后调用perror() 是没有用的或不合适的。在任何其他情况下,perror() 将报告的errno 的值不能反映select() 返回的原因。
  • 请注意,您将EINTR 视为超时而不是重试。将if (res &lt; 0 &amp;&amp; errno != EINTR) 更改为if (res &lt; 0) { if (errno != EINTR) { ... } }

标签: c sockets tcp network-programming embedded-linux


【解决方案1】:

根据 select() 手册页:

select() returns the number of ready descriptors that are contained in
the descriptor sets, or -1 if an error occurred.  If the time limit
expires, select() returns 0.

...所以如果 select() 返回 0,那是因为在超时之前没有完成任何 I/O 操作。

至于为什么没有完成 I/O 操作:如果您正在等待 TCP 连接完成,那么最可能的解释是 TCP 连接尚未完成(可能是因为速度慢、过载或网络或服务器损坏?)。

另一个(不太可能,但可能)的解释可能是您在 Windows 下运行程序,并且在 Windows 下,如果非阻塞 connect() 失败,则通过在 中设置一个位来指示该失败异常 fd_set(即作为第四个参数传递给select(),就在超时参数之前的那个)。在发布的代码中,您为该参数传递了 NULL,这意味着在 Windows 下,您无法知道您的非阻塞 TCP 连接尝试何时失败。 (在其他操作系统下,连接失败会导致套接字选择为read-for-read和ready-for-write,这使得连接失败更容易反应)

【讨论】:

  • 所以重申一下:基本上Operation now in progress 仍然表明连接到服务器的尝试正在进行中,如果它花费的时间比预期的要长,它可能是服务器信息(IP/端口)是不正确或由于某种原因服务器需要足够长的时间才能将 ACK 发送回客户端?
  • 此代码无法在 Windows 上运行,因为fcntl() 在 Windows 上不存在。
  • 不,@xyf,在报告超时的情况下,您的代码打印的“正在运行”消息没有意义。只有在文档说可以的情况下,您才可以依赖 errno 的值——通常是在函数调用返回 -1 以指示错误时,如 select() 的情况。
  • 了解Operation now in progress 并不是真正表示错误,而是表明select 未能在所述文件描述符上找到任何活动,这应该暗示connect() 没有获得有效来自服务器的响应?;
  • @xyf errnoconnect() 返回-1 时由connect() 调用设置; select() 保持不变,因为 select() 从未返回 -1。所以在这种情况下perror() 打印出的错误字符串与select() 的行为无关。
【解决方案2】:

我的理解是在 connect() 之后设置了非阻塞模式,以防它没有立即连接,getsockopt() 指示 connect() 调用是否确实建立但由于某种原因,select 似乎返回 0。它有延迟太小?

非阻塞必须在socket(2) 调用之后和connect(2) 调用之前设置,否则连接将被阻塞(无法到达select() 调用)直到connect(2 ) 失败。这通常超过两分钟,而这个技巧只需等待 5 秒。在 connect 调用中。

对于远程站点的远程连接,5 秒的延迟通常很小。在局域网中,如果您在 5 秒内没有连接。那么就说明有问题了。

我敢打赌,你正在尝试连接到一个不可用的套接字(不存在的主机,检查服务器是否正在侦听地址:你尝试连接的端口),你有忘记将 sockaddr_in 结构中的某些字段转换为净字节顺序(这在您的 sn-p 中似乎是正确的)或防火墙阻止您连接(这可能是问题)。您正在等待socketFd 可用于写入,这是正确的,因为如果未先连接连接,则不会(并且阻塞),因此显然您正在正确地做事,因此某些地址拼写错误或者防火墙正在切断对服务器的访问。

无论哪种方式,select 中的超时都不是错误,而只是超时。您正在使用的软件正在考虑 5 秒的超时。套接字中的致命错误,因此您必须询问开发人员或检查您的网络连接。

【讨论】:

  • 对于 UDP,我看到它可以很好地传输数据,但它的 recvfrom 正在超时并出现错误 Resource temporarily unavailable 。你明白为什么吗?如果能正常传输,那服务器设置也应该没问题
猜你喜欢
  • 2013-03-19
  • 1970-01-01
  • 2022-12-27
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2022-12-28
  • 2021-05-12
  • 2021-09-08
相关资源
最近更新 更多