1、实现代码
#include "csapp.h"
void echo(int connfd);
void command(void);
int main(int argc, char **argv)
{
int listenfd, connfd, port, clientlen;
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);
FD_ZERO(&read_set);
FD_SET(STDIN_FILENO,&read_set);
FD_SET(listenfd,&read_set);
while (1)
{
ready_set=read_set;
Select(listenfd+1,&read_set,NULL,NULL,NULL);
if(FD_ISSET(STDIN_FILENO,&read_set)){
command(); //从标准输入读命令行
}
if(FD_ISSET(listenfd,&ready_set)){
clientlen = sizeof(clientaddr);
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
/* 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);
}
}
}
void command(void)
{
char buf[MAXLINE];
if(!Fgets(buf,MAXLINE,stdin))
exit(0);
printf("%s",buf);
}
2、代码分析
一开始先用open_listenfd函数打开一个监听描述符,然后使用FD_ZERO创建一个空的读集合。
之后定义由描述符0和描述符3组成的读集合。
之后开始服务器循环,但是不调用accept函数等待一个连接请求,而是调用select函数,这个函数会一直阻塞,直到监听描述符或标准输入准备好可以读,比如用户按下回车键,select函数会返回ready_set的值。
一旦select函数返回,就用FD_ISSET宏指令来确定哪个描述符准备好了可以读,如果是标准输入准备好了就调用command函数。如果是监听描述符准备好了,就调用accept函数,得到一个已连接描述符,然后调用echo函数。
这个服务器一旦连接到某个客户端就会连续会送输入行,直到客户端关闭,这个时候键入一个命令到标准输入不会得到响应。一个更好的方法是,更细粒度的多路复用。