选择不等待 C++ 套接字中的超时值

select does not wait for timeout value in c++ sockets

本文关键字:超时 套接字 等待 C++ 选择      更新时间:2023-10-16

我编写了一个服务器代码,它接受来自客户端的新客户端和数据。但问题是,尽管没有来自客户端的数据,但选择不会等到超时。我想等待 5 秒钟并为可用客户端发送心跳。但它在第一次迭代中等待 5 秒,然后在下一次迭代中快速发送检测信号。如何解决这个问题。提前谢谢。

void * Communicate(void * id)
{
int *iSockID = (int *) id;
int listener =  *iSockID;
fd_set master;    // master file descriptor list
fd_set read_fds;  // temp file descriptor list for select() read
fd_set write_fds; // temp file descriptor list for select() read
int fdmax;        // maximum file descriptor number
int i, j, rv;
FD_ZERO(&master);    // clear the master and temp sets
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
// add the listener to the master set
FD_SET(listener, &master);
printf("Listener is %d n" , listener);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
//accept 3 clients

// main loop
for(;;) {
    read_fds = master; // copy it
    write_fds = master;
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv) ;
    if (iResult == -1) 
    {
        perror("select");
        exit(4);
    }
    for(i = 0; i <= fdmax; i++) 
    {           
        //send work for clients
        SendHeartBeats(write_fds , fdmax , listener , i );
    }
    // run through the existing connections looking for data to read
    // ADD NEW CONNECTIONS READ FROM CONNECTIONS    
    for(i = 0; i <= fdmax; i++)
    {   

        if (FD_ISSET(i, &read_fds)) 
        { // we got one!!
            // handle new connections               
            if (i == listener) 
            {                                    
                AcceptNewClients(master , fdmax , listener );   
            } else 
            {
                AccepeDataFromClients(i , master);
            } // END handle data from client
        } // END got new incoming connection
    } // END looping through file descriptors
    sleep(3);
} // END for(;;)
return 0;
}

不能按等于fd_set变量。 您需要使用FD_COPY。 如果不这样做,则只需将句柄复制到实际数据,该句柄已标记为已完成。

您需要

更改代码以在每次调用select()时重新初始化read_fdwrite_fd变量,因为它会在退出时修改它们,因此每次都需要重置它们。 正如其他人所说,使用 = 运算符复制 master 变量并不是复制fd_set结构的正确方法。

试试这个:

void * Communicate(void * id)
{
    int *iSockID = (int *) id;
    int listener =  *iSockID;
    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select() read
    fd_set write_fds; // temp file descriptor list for select() read
    struct timeval tv;
    int fdmax;        // maximum file descriptor number
    int i, j, rv;
    printf("Listener is %d n", listener);
    // add the listener to the master set
    FD_ZERO(&master);
    FD_SET(listener, &master);
    // keep track of the biggest file descriptor
    fdmax = listener; // so far, it's this one
    //accept 3 clients
    // main loop
    clock_t c1 = clock();
    while (1)
    {
        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);
        #ifdef MSWINDOWS
        // Windows does not have FD_COPY()
        for (u_int i = 0; i < master.fd_count; ++i)
        {
            FD_SET(master.fd_array[i], &read_fd);
            FD_SET(master.fd_array[i], &write_fd);
        }
        #else
        FD_COPY(&master, &read_fd);
        FD_COPY(&master, &write_fd);
        #endif
        tv.tv_sec = 1;
        tv.tv_usec = 0;
        int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv);
        if (iResult == -1) 
        {
            perror("select");
            exit(4);
        }
        clock_t c2 = clock();
        if (((c2-c1) / CLOCKS_PER_SEC) >= 5)
        {
            c1 = c2;
            for(i = 0; i <= fdmax; i++) 
            {           
                if ((i != listener) && FD_ISSET(i, &write_fds))
                { 
                    //send work for client
                    SendHeartBeat(i);
                }
            }
        }
        // run through the existing connections looking for data to read
        // ADD NEW CONNECTIONS READ FROM CONNECTIONS    
        for(i = 0; i <= fdmax; i++)
        {   
            if (FD_ISSET(i, &read_fds)) 
            {
                // we got one!!
                if (i == listener) 
                {                                    
                    AcceptNewClient(master, fdmax, listener);   
                }
                else 
                {
                    AcceptDataFromClient(i);
                }
            }
        }
    }
    return 0;
}

您在评论中指出您正在使用Linux 2.6.27.由于在 Linux 中,fd_set是包含整型数组的结构,因此使用赋值将master复制到read_fds就可以了。

您的select调用未阻塞的原因是,您正在初始化read_fdswrite_fds master 。您的空闲客户端套接字始终是可写的,因此您会立即醒来。顺便说一句,选择在listener套接字上可写是没有意义的。

只有在尝试写入但收到 EAGAIN/EWOULDBLOCK 错误时,才应在write_fds中设置客户端套接字。