使用select实现的loop函数
loop函数导致服务进程进入无限死循环。我们将会看到这个函数的两个版本。下面的代码显示了使用select的版本。后面的代码将会显示使用poll的版本。
#include "opend.h"
#include <sys/time.h>
#include <sys/select.h>
void loop(void)
{
int i, n, maxfd, maxi, listenfd, clifd, nread;
char buf[MAXLINE];
uid_t uid;
fd_set rset, allset;
FD_ZERO(&allset);
/* obtain fd to listen for client requests on */
if ((listenfd = serv_listen(CS_OPEN)) < 0)
log_sys("serv_listen error");
FD_SET(listenfd, &allset);
maxfd = listenfd;
maxi = -1;
for ( ; ; ) {
rset = allset; /* rset gets modified each time around */
if ((n = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0)
log_sys("select error");
if (FD_ISSET(listenfd, &rset)) {
/* accept new client request */
if ((clifd = serv_accept(listenfd, &uid)) < 0)
log_sys("serv_accept error: %d", clifd);
i = client_add(clifd, uid);
FD_SET(clifd, &allset);
if (clifd > maxfd)
maxfd = clifd; /* max fd for select() */
if (i > maxi)
maxi = i; /* max index in client[] array */
log_msg("new connection: uid %d, fd %d", uid, clifd);
continue;
}
for (i = 0; i <= maxi; i++) { /* go through client[] array */
if ((clifd = client[i].fd) < 0)
continue;
if (FD_ISSET(clifd, &rset)) {
/* read argument buffer from client */
if ((nread = read(clifd, buf, MAXLINE)) < 0) {
log_sys("read error on fd %d", clifd);
} else if (nread == 0) {
log_msg("closed: uid %d, fd %d",
client[i].uid, clifd);
client_del(clifd); /* client has closed cxn */
FD_CLR(clifd, &allset);
close(clifd);
} else { /* process client's request */
request(buf, nread, clifd, client[i].uid);
}
}
}
}
}
这个函数调用serv_listen创建服务器端用于接收客户连接请求。剩下的部分就是一个循环,循环首先使用select调用。select返回的时候,可以确保两件事情:
- 文件描述符号listenfd已经准备好了读取,也就是说,有某个新的客户进程调用了cli_conn。为了处理这个,我们调用serv_accept并且之后更新客户进程的数组以及相关的记录索引信息。(我们保存最大的文件描述符号号码以便传递select的第一个参数,我们也保存客户数组中被使用元素的最大索引)
- 一个已经存在的客户连接准备好了被读取。这个意思是说,客户进程要么结束了,要么发送了一个新的请求。我们通过read返回0(文件结束符号)来确定客户进程已经终止。如果read返回正数,那么表明需要处理一个新的请求,这个请求通过我们调用request来进行处理。
我们在allset文件描述符号集合中保存当前使用的文件描述符号。一个新的客户连接到服务器上的时候,会打开这个文件描述符号集合中的合适的位;当客户进程终止的时候,会关闭相应其中合适的位。
无论客户进程的终止是自发的还是非自发的,当它终止的时候,我们经常能够知道,因为所有的客户的文件描述符号(包括连接到服务进程的)都会被内核自动地关闭。这一点和XSI的IPC机制有所不同。
网友评论