#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;i<FD_SETSIZE;i++) p->clientfd[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;i<FD_SETSIZE;i++) if(p->clientfd[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 <port>\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數組的最大索引。app
check_clients函數回送來自每一個準備好的已鏈接描述符的一個文本行,若是成功地從描述符讀取了一個文本行,那麼就將該文本行回送到客戶端。若是由於客戶端關閉鏈接中地它的那一端,檢測到EOF,那麼將關閉這邊地鏈接端,並從池中清除這個描述符。dom
根據有限狀態機模型,select函數檢測到輸入事件,add_client函數建立一個新的邏輯流,check_client函數回送輸入行,從而執行狀態轉移,並且當客戶端完成文本行發送時,它還要刪除這個狀態機。函數