09计应一班 闫俊霖
/* server.c */
#include
#include
#include
#include
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
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];
char str[INET_ADDRSTRLEN];
socklen_t cliaddr_len;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0); /*打开一个网络通讯端口返回一个套接字描述符赋值给listenfd*/
bzero(&servaddr, sizeof(servaddr)); /*将结构体清零*/
servaddr.sin_family = AF_INET; /*设置地址类型为AF_INET*/
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*设置网络地址为INADDR_ANY,这个宏表示本地的任意ip地址*/
servaddr.sin_port = htons(SERV_PORT); /*设置端口号为SERV_PORT,我们定义为8000*/
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); /*将所监听的端口套接字描述符与服务器的地址绑定*/
Listen(listenfd, 20); /*声明服务器处于监听状态,并且最多允许有20个客户端处于连接待状态*/
maxfd = listenfd; /* initialize 将所监听的最大的套接字描述符赋给maxfd*/
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++) /*将client[FD_SETSIZE]的值设为-1*/
client[i] = -1; /* -1 indicates available entry */
FD_ZERO(&allset); /*将allset套接字描述符集清空*/
FD_SET(listenfd, &allset); /*将所监听的套接字描述符添加到allset*/
for ( ; ; ) { /*用于循环接受数据请求,要与服务器交互的client的端口*/
rset = allset; /* structure assignment 把allset套接字描述符集的内容赋给rset*/
nready = select(maxfd+1, &rset, NULL, NULL, NULL); /*select用于监听多个阻塞的文件描述符,当这些被阻塞的端口有数据请求或要与服务器交互时,select就会返回请求客户端的个数给nready,若没有,则返回0,若select调用出错,则会返回一个负值。由于最后一个时间参数值是null,所以select会一直阻塞等待着client的请求。maxfd+1表示集合中描述符的范围即所有描述符的最大值加1,rset则是在关注哪个客户端有数据可读了,就把客户端的套接字描述符添加在rset集合中。*/
if (nready < 0) /*判断select是否调用成功,若出错返回一个负数给nready*/
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* new client connection 判断套接字listenfd是否在rset中,该数据请求的客户端是否在监听的队列中。*/
cliaddr_len = sizeof(cliaddr); /*把cliaddr结构体的长度赋给cliaddr_len,作为缓冲区的长度*/
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); /*接受客户端的连接请求,与客户端建立连接*/
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); /*打印客户端的ip地址和端口号*/
for (i = 0; i < FD_SETSIZE; i++) /*将文件描述符connfd的值存入client[FD_SETSIZE]中最小未被使用的位置*/
if (client[i] < 0) {
client[i] = connfd; /* save descriptor */
break;
}
if (i == FD_SETSIZE) { /*若i等于FD_SETSIZE,则表示有数据请求的客户端已达到FD_SETSIZE最大值*/
fputs("too many clients\n", stderr);
exit(1);
}
FD_SET(connfd, &allset); /* add new descriptor to set 向allset中添加与服务器建立连接并有数据请求的客户端端口*/
if (connfd > maxfd) /*若此时建立并有数据请求的客户端已大于原来的套接字描述符最大值,则将connfd赋值给maxfd*/
maxfd = connfd; /* for select */
if (i > maxi) /*把maxi赋值为当前最大的放置建立连接并有数据请求客户端描述符的索引,以在下面的for循环语句中作为处理客户端请求个数的上限;*/
maxi = i; /* max index in client[] array */
if (--nready == 0) /*若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,进行下一轮的select循环;*/
continue; /* no more readable descriptors */
}
for (i = 0; i <= maxi; i++) { /* check all clients for data 依次处理有数据请求的客户端*/
if ( (sockfd = client[i]) < 0) /*将client[i]中存放的客户端的套接字描述符赋给sockfd,若小于0,则表示client[i]中没有套接字描述符。*/
continue;
if (FD_ISSET(sockfd, &rset)) { /*判断sockfd是否在rset这个描述符集中*/
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { /*读入客户端的数据,若n等于0表示客户端已经关闭了连接*/
/* connection closed by client */
Close(sockfd); /*服务器关闭与客户端相应的连接*/
FD_CLR(sockfd, &allset); /*从fd_set结构:allset中删掉清空与客户端连接的套接字描述符*/
client[i] = -1; /*将存放该客户端套接字的数组位置设为-1,存放新的客户端连接描述符*/
} else {
int j;
for (j = 0; j < n; j++) /*依次读取客户端输入的数据*/
buf[j] = toupper(buf[j]); /*将客户端发来的数据改为大写*/
Write(sockfd, buf, n); /*将数据发回客户端*/
}
if (--nready == 0) /*判断--nready,若--nready为0,则表示当前的套接字描述符集中只有listenfd这个监听的描述符,没有客户端的数据请求端口,进行下一轮的select循环;*/
break; /* no more readable descriptors */
}
}
}
}
/*
执行:select是网络程序中很常用的一个系统调用,它可以同时监听多个阻塞的文件描述符。
1.服务器监听客户端的请求,将这些请求都放在一个队列中
2.当这些被阻塞的端口有数据请求或要与服务器交互时,select就会返回请求客户端的个数给nready,若没有,则返回0,若select调用出错,则会返回一个负值。由于最后一个关于时间的参数值是null,所以select会一直阻塞等待着client的请求,直到有数据交互的客户端产生。
3.当只有listenfd时,没有数据到达的时,则一直监听
4.当rset中有listenfd ,并有connfd时客户端与服务器连接从中读取数据并转换发回客户端
*/
阅读(2146) | 评论(0) | 转发(2) |