我正在阅读R.Stevens的《 UNIX网络编程》,下面的代码片段仅包含相关部分。这是一个回显服务器。
fd_set rset, allset;
int nready, client[FD_SETSIZE];
...
for( ; ; ) {
rset = allset;
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
....
for( i = 0; i <= maxi; i++){
if ( (sockfd = client[i]) <= 0)
continue;
if (FD_ISSET(sockfd, &rset)){
if( (n = read(sockfd, buf, MAXLINE)) == 0){
close(sockfd);
FD_CLEAR(sockfd, &allset);
client[i] = -1;
} else
writen(sockfd, buf, n);
...
}
}
我将简要描述变量:client
是一个数组,其中包含分配给已连接客户端的文件描述符; -1表示自由条目。 nready
是可供读取的 fd 的数量。 rset
是一种保留位的结构,用于指定准备就绪的 fd 。 allset
是相同类型的结构,表示必须由select()
测试的 fd 。
内部for
循环检查来自每个已连接客户端的传入数据(这通过FD_ISSSET宏进行了测试)。如果有任何待处理的数据,服务器会将其写回到客户端。如果read()
返回0,则表示客户端已向服务器发送FIN,因此它将终止与close()
的连接。
现在是问题:作者说我刚才展示的服务器有问题。确实,考虑一个恶意客户端连接到服务器,发送一个字节的数据(而不是换行符),然后进入睡眠状态。服务器将调用read()
,读取该字节,然后在下一次对read()
的调用中阻塞,等待来自此客户端的更多数据,从而拒绝为所有其他客户端提供服务。我不明白为什么服务器应该在下一次对read()
的调用中阻塞:在下一次对read()
的调用之前,select()
将为每个连接的套接字返回就绪状态,并且客户端没有发送更多数据(该字节已被占用),该客户端的就绪状态位未设置。因此,不会输入客户端的if
块,也不会调用read()
。很好,除非select()
可以为没有数据排队的套接字返回就绪状态,我认为这是不可能的。那我在哪里错了?