#include "csapp.h"
typedef struct{
int maxfd;
fd_set read_set;
fd_set ready_set;
int nready;
int maxi;
int clientfd[FD_SETSIZE];
rio_t clientrio[FD_SETSIZE];
}pool;
int byte_cnt=0;
void echo(int connfd);
void init_pool(int listenfd,pool *p)
{
int i;
p->maxi=-1;
for(i=0;iclientfd[i]=-1;
p->maxfd=listenfd;
FD_ZERO(&p->read_set);
FD_SET(listenfd,&p->read_set);
}
void add_client(int connfd,pool *p)
{
int i;
p->nready--;
for(int i=0;iclientfd[i]<0){
p->clientfd[i]=connfd;
Rio_readinitb(&p->clientrio[i],connfd);
FD_SET(connfd,&p->read_set);
if(connfd>p->maxfd)
p->maxfd=connfd;
if(i>p->maxi)
p->maxi=i;
break;
}
if(i==FD_SETSIZE)
app_error("add_client error :to many clients");
}
void check_clients(pool *p)
{
int i,connfd,n;
char buf[MAXLINE];
rio_t rio;
for(i=0;(i<= p->maxi)&&(p->nready > 0);i++){
connfd=p->clientfd[i];
rio = p->clientrio[i];
if((connfd > 0)&&(FD_ISSET(connfd,&p->ready_set))){
p->nready--;
if((n=Rio_readlineb(&rio,buf,MAXLINE))!=0){
byte_cnt+=n;
printf("Server receive %d (%d total) bytes on fd %d\n",n,byte_cnt,connfd);
Rio_writen(connfd,buf,n);
}
else
{
Close(connfd);
FD_CLR(connfd,&p->read_set);
p->clientfd[i]=-1;
}
}
}
}
int main(int argc, char **argv)
{
int listenfd, connfd, port, clientlen;
static pool pool;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp;
fd_set read_set,ready_set;
if (argc != 2)
{
fprintf(stderr, "usage: %s \n", argv[0]);
exit(0);
}
port = atoi(argv[1]);
// Signal(SIGCHLD,sigchld_handler);
listenfd = Open_listenfd(port);
init_pool(listenfd,&pool);
// FD_ZERO(&read_set);
// FD_SET(STDIN_FILENO,&read_set);
// FD_SET(listenfd,&read_set);
while (1)
{
pool.ready_set=pool.read_set;
pool.nready=Select(pool.maxfd+1,&pool.ready_set,NULL,NULL,NULL);
//if(FD_ISSET(STDIN_FILENO,&read_set)){
// command(); //从标准输入读命令行
//}
if(FD_ISSET(listenfd,&pool.ready_set)){
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
add_client(connfd,&pool);
/* determine the domain name and IP address of the client */
/* hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
echo(connfd);
Close(connfd);*/
}
check_clients(&pool);
}
创建一个pool结构维护着活动客户端的集合,在调用init_pool初始化池之后,服务器进入一个无限循环,在循环的每次迭代中服务器调用select函数来检测两种不同类型的输入事件,a)来自一个新客户端的连接请求到达。b)一个已存在的客户端已连接描述符准备好了可读。当一个连接请求到达时,服务器打开链接,调用add_client函数,将该客户端添加到池里,最后服务器调用check_clients函数,把来自每个准备好的已连接描述符的一个文本行回送回去。
init_pool函数初始化客户端池,clientfd数组表示已连接描述符的集合,其中整数-1表示一个可用的槽位,初始时,已连接描述符集合是空的,而监听描述符是select读集合中唯一的描述符。
add_client函数添加一个新的客户端到活动客户端池中,在clientfd数组中找到一个空槽位置后,服务器将这个已连接描述符添加到数组中,并初始化相对应的RIO读缓冲区,这样就可以对这个描述符调用rio_readlineb。然后,将这个已连接描述符添加到select读集合并更新池的一些全局属性,maxfd变量记录了select的最大文件描述符,maxi变量记录的是到clientfd数组的最大索引。
check_clients函数回送来自每个准备好的已连接描述符的一个文本行,如果成功地从描述符读取了一个文本行,那么就将该文本行回送到客户端。如果因为客户端关闭连接中地它的那一端,检测到EOF,那么将关闭这边地连接端,并从池中清除这个描述符。
根据有限状态机模型,select函数检测到输入事件,add_client函数创建一个新的逻辑流,check_client函数回送输入行,从而执行状态转移,而且当客户端完成文本行发送时,它还要删除这个状态机。