Unixnetworking编程说明

当我偶然发现这个程序时,我正在阅读经典的“ Unixnetworking编程”一书(6.8节,第179-180页)

#include "unp.h" int main(int argc, char **argv) { int i, maxi, maxfd, listenfd, connfd, sockfd; int nready, client[FD_SETSIZE]; ssize_t n; fd_set rset, allset; char buf[MAXLINE]; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); maxfd = listenfd; /* initialize */ maxi = -1; /* index into client[] array */ for (i = 0; i < FD_SETSIZE; i++) client[i] = -1; /* -1 indicates available entry */ FD_ZERO(&allset); FD_SET(listenfd, &allset); for ( ; ; ) { rset = allset; /* structure assignment */ nready = Select(maxfd+1, &rset, NULL, NULL, NULL); if (FD_ISSET(listenfd, &rset)) { /* new client connection */ clilen = sizeof(cliaddr); connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); for (i = 0; i < FD_SETSIZE; i++) if (client[i] < 0) { client[i] = connfd; /* save descriptor */ break; } if (i == FD_SETSIZE) err_quit("too many clients"); FD_SET(connfd, &allset); /* add new descriptor to set */ if (connfd > maxfd) maxfd = connfd; /* for select */ if (i > maxi) maxi = i; /* max index in client[] array */ if (--nready <= 0) continue; /* no more readable descriptors */ } for (i = 0; i <= maxi; i++) { /* check all clients for data */ if ( (sockfd = client[i]) < 0) continue; if (FD_ISSET(sockfd, &rset)) { if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { /*4connection closed by client */ Close(sockfd); FD_CLR(sockfd, &allset); client[i] = -1; } else Writen(sockfd, buf, n); if (--nready <= 0) break; /* no more readable descriptors */ } } } } 

作者提到这个程序对于DOS攻击是不安全的。 从书中引用,

“不幸的是,我们刚刚展示的服务器存在问题,请考虑如果恶意客户端连接到服务器,发送一个字节的数据(除了换行符),然后进入睡眠状态,服务器将调用read (系统调用),它将从客户端读取单个字节的数据,然后在下一次调用中阻塞读取 ,等待来自这个客户端的更多数据。服务器然后被这个客户端阻塞,并且不会服务任何其他客户端,直到恶意客户端发送换行符或终止“

我不确定我是否正确理解这一点。 为什么系统调用是第二次调用这个恶意客户端,因为它只发送1字节的数据,第一次调用通知select 。 随后的select调用将永远不会有这个恶意文件描述符设置,因为没有任何活动。 我在这里错过了什么?

我的猜测是代码中有一个错字,而不是Read,它应该是本书其他地方提到的Readline方法的一些版本。

注意 :代码包含ReadSelect (大写字母R和S),它们只是select系统调用的error handling包装

是的,这似乎是有意成为Readline

在可下载的源代码文件是tcpcliserv/tcpservselect01.c并且有一个相应的.lc文件(带有行号注释),它使用Readline而不是Read ,在本书的第二版( 源代码 )中是Readline 。 关于唯一能够理解“括号”(除了换行符)意思的方法是假定预期的读取函数读取换行符。

奇怪的是, 在勘误中还没有报道。 也许你应该这样做。

我认为他指出的问题是,正如你在注释中提到的那样,这段代码使用Read作为read的包装。 我的猜测是,因为我现在不打算把这本书的副本挖出来,所以Read会尝试再次read完成的数据。