为什么select()总是在第一次超时后返回0

为什么select()总是在第一次超时后返回0,c,linux,sockets,C,Linux,Sockets,当我在Linux套接字程序上工作时,我对选择函数有一个问题。如果客户端在服务器配置的时间间隔内连接到服务器端,则select函数工作正常,如手册页所示。如果发生超时,select函数将永远返回0。当时,我调试了客户机,发现客户机已连接到服务器。但是select函数仍然返回0。我已经搜索了这个问题,但没有发现任何有用的。有人知道select为什么这样做吗?我的linux版本是RHEL5.4。谢谢你的帮助 代码如下所示 static const int maxLog = 10000; int ma

当我在Linux套接字程序上工作时,我对选择函数有一个问题。如果客户端在服务器配置的时间间隔内连接到服务器端,则select函数工作正常,如手册页所示。如果发生超时,select函数将永远返回0。当时,我调试了客户机,发现客户机已连接到服务器。但是select函数仍然返回0。我已经搜索了这个问题,但没有发现任何有用的。有人知道select为什么这样做吗?我的linux版本是RHEL5.4。谢谢你的帮助

代码如下所示

static const int maxLog = 10000;

int main()
{
    int servSock;
    signal(SIGPIPE, SIG_IGN);
    if((servSock = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        printf("socket create fail\n");
        exit(-1);   
    }
    int val = 1;
    if(setsockopt(servSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))<0)
    {
        DieWithUserMessage("setsockopt error");
    }

    struct sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(22000);

    if(bind(servSock, (struct sockaddr *) &serverAddr, 
                sizeof(serverAddr)) < 0)
    {
        printf("socket bind  fail\n");
        exit(-1);   
    }

    if(listen(servSock, maxLog) < 0)
    {
        printf("listen failed\n");
        exit(-1);
    }   

    fd_set read_set;
    FD_ZERO(&read_set);
    FD_SET(servSock, &read_set);
    int maxfd1 = servSock + 1; 
    std::set<int> fd_readset;

    for(;;){    
        struct timeval tv;
        tv.tv_sec = 5;
        int ret = select(maxfd1, &read_set, NULL, NULL, tv);       
        if(ret == 0)
            continue;

        if(ret < 0)
            DieWithUserMessage("select error");

        if(FD_ISSET(servSock, &read_set))
        {
            struct sockaddr_in clntAddr;
            socklen_t clntAddrlen = sizeof(clntAddr);
            int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrlen);
            if(clntSock < 0)
            {
                printf("accept failed()");
                exit(-1);
            }   

            maxfd1 = 1 +  (servSock>=clntSock? servSock:clntSock);
            FD_SET(clntSock, &read_set );
            fd_readset.insert(clntSock); 
         }

    } 
}
static const int maxLog=10000;
int main()
{
int-servSock;
信号(信号管、信号灯);
if((servSock=socket(AF\u INET,SOCK\u STREAM,0))<0)
{
printf(“套接字创建失败\n”);
出口(-1);
}
int-val=1;
if(setsockopt(servSock,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))=clntSock?servSock:clntSock);
FD_集(clntSock和read_集);
fd_readset.insert(clntSock);
}
} 
}

该函数使用起来令人沮丧;每次调用它之前都必须设置它的参数,因为它会修改它们。您所看到的是一个演示,演示了如果不在循环中每次设置fd_集会发生什么。

您必须在每次迭代中填充fd_集。最好的方法是在某处维护您的FD集合,并将select调用所需的FD集合放入临时FD_集合中

如果需要处理大量客户端,可能必须更改FD_SETSIZE(在
/usr/include/sys/select.h
中)宏


快乐网络编程:)

您已经有了正确的答案-在每次调用
选择(2)
之前重新初始化
fd\u集

我想为您指出一个更好的选择——Linux提供了一个功能。虽然它不是标准的,但它更方便,因为您只需要设置等待一次的事件。内核为您管理文件描述符事件表,因此效率更高
epoll
还提供边缘触发功能,其中仅对描述符上的状态更改发出信号

为了完整性,BSD提供了Solaris


还有一件事:您的代码在客户端和服务器之间有一个众所周知的竞争条件。看看。

如果在每次调用select之前不重置timeval结构,同样的效果似乎也会发生。

我在类似的代码中遇到了同样的问题。在每次调用select()之前,我都会按照建议进行初始化,这样做很有效。在这种情况下的代码中,只要将两行引入循环就可以工作

FD_ZERO(&read_set);
FD_SET(servSock, &read_set);

谢谢你的帮助。我按照您的建议在循环开始时重置了fs_设置,现在可以正常工作了。谢谢!我在这件事上大发雷霆。
poll
在POSIX中是标准化的。但是它没有比
select
更好。一般建议:使用poll()而不是select()。您必须在
select
中使用
&tv
而不是
tv
,POSIX说
select()
函数可以修改超时参数。据推测,如果您所在的系统确实对其进行了修改,则其修改后的值会记录到超时过期之前的剩余时间,如果超时确实过期,则该时间为零,因此在第一次迭代后,调用会退化为轮询,因为归零的超时表示“立即返回”,而不是“必要时等待”。