《UNIX网络编程 卷1》 笔记: 使用select函数的单进程TCP回显服务器程序

在TCP 客户/服务器程序示例这一节中,我们编写了一个多进程的TCP回显服务器程序,本节我们使用select函数监听服务器套接字和所有的客户的套接字来实现同样的功能。具体实现就是每当一个新的客户建立连接,我们把它的套接字描述符(为了与服务器程序自身的监听描述符作区分,我称服务器程序为服务客户创建的套接字为“客户套接字”)保存在一个数组中,然后将该描述符加入select函数监听的读描述符集中,然后循环处理所有的读事件。具体代码如下:

 

#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;
	maxi = -1;
	for (i = 0; i < FD_SETSIZE; i++)
		client[i] = -1;
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset); /*将服务器套接字描述符加入读描述符集中*/
	for ( ; ; ) {
		rset = allset;
		nready = Select(maxfd + 1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) { /*新的客户连接*/
			clilen = sizeof(cliaddr);
			connfd = Accept(listenfd, (SA *)&cliaddr, &clilen);
			/*找到第一个未使用的项,存入客户套接字描述符*/
			for (i = 0; i < FD_SETSIZE; i++) { 
				if (client[i] < 0) {
					client[i] = connfd;
					break;
				}
			}
			if (i == FD_SETSIZE)
				err_quit("too many clients");
			FD_SET(connfd, &allset);
			if (connfd > maxfd)
				maxfd = connfd;
			if (i > maxi)
				maxi = i;
			if (--nready <= 0)
				continue;
		}

		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_CLR(sockfd, &allset);
					client[i] = -1;
				} else 
					Writen(sockfd, buf, n);
				if (--nready <= 0)
					break;
			}
		}
	}
}

书中说这段代码有拒绝服务的缺陷,是说服务器调用read函数阻塞于某一个用户的套接字上,不能服务其他的客户。我实际试验了下没有发现有这个缺陷,也看不出来代码哪里有拒绝服务的缺陷。

 

这段代码有一个缺陷就是处理客户的套接字时没有考虑异常情况。比如说客户异常终止,给服务器发送RST,那么代码里的Read(注意是包裹函数)函数会返回“read error: Connection reset by peer”,服务器进程会终止。下节使用poll函数实现的版本会修正这个缺陷。

 

你可能感兴趣的:(UNIX网络编程,卷1,Linux,网络编程,select,tcp)