09级计算机应用1班 段彦青 0906041001
/* server.c */
#include
#include /*库函数*/
#include /*字符串处理*/
#include /*INTERNET地址族*/
#include "wrap.h" /*包含wrap.h 头文件*/
#define MAXLINE 80 /*宏定义 定义MAXLINE为80*/
#define SERV_PORT 8000 /*宏定义 定义端口号为8000*/
int main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd; /*定义整型变量*/
int nready, client[FD_SETSIZE]; /*定义client数组*/
ssize_t n; /*定义signed size_t类型变量*/
fd_set rset, allset; /*定义fd_set(文件描述符的集合)型变量*/
char buf[MAXLINE]; /*定义缓冲区数组*/
char str[INET_ADDRSTRLEN]; /*定义字符数组*/
socklen_t cliaddr_len; /*定义socklen_t类型变量*/
struct sockaddr_in cliaddr, servaddr; /*定义struct sockaddr_in 类型变量*/
listenfd = Socket(AF_INET, SOCK_STREAM, 0); /*分配一个文件描述符*/
/*初始化servaddr*/
bzero(&servaddr, sizeof(servaddr)); /*取servaddr的地址,并将结构体清零*/
servaddr.sin_family = AF_INET; /*设置地址类型为AF_INET (IPV4)*/
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)); /*绑定要结合的网络地址与端口号,bind()成功返回0,失败返回-1 */
Listen(listenfd, 20); /*声明listened处于监听状态, 最多允许有20个客户端处于连接待状态,listen()成功返回0,失败返回-1*/
maxfd = listenfd; /* maxfd初始化 */
maxi = -1; /* maxi赋初值-1 */
for (i = 0; i < FD_SETSIZE; i++) /* FD_SETSIZE*/
client[i] = -1; /*将client[]数组的元素全部赋值为-1,-1代表可进入的 */
FD_ZERO(&allset); /*清空allset集合,用于存放soket文件描述符*/
FD_SET(listenfd, &allset); /*把监听到的lisenfd放入allset集合中*/
for ( ; ; ) {
rset = allset; /* structure assignment */
nready = select(maxfd+1, &rset, NULL, NULL, NULL); /*调用select函数它可以同时监听多个阻塞的文件描述符,哪个有数据到达就处理哪个,返回值大于0,表示有文件可读,就可以用FD_ISSET来遍历所有可能的描述符,以检查是哪个上面有活动发生。如果是监听套接字可读,说明正有一个客户试图建立连接,此时,即可调用accept。没有数据到达时就阻塞。反之,如果是某个客户描述符准备好,则说明该描述符上有一个客户请求需要我们读取和处理。maxfd是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1。*/
if (nready < 0)
perr_exit("select error"); /*nready小于0时出错*/
if (FD_ISSET(listenfd, &rset)) { /* 检测文件描述符是否在reset中,检测是否有新连接出现 */
cliaddr_len = sizeof(cliaddr); /*取客户端的地址长度放入cliaddr_len中*/
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); /*服务器调用accept()接受连接,如果没有客户端的连接请求,就阻塞等待直到有客户端连接上来,返回客户端的地址和端口号,赋值给connfd*/
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port)); /*打印客户端网络地址和端口号*/
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0) {
client[i] = connfd; /* 保存里连接的客户端的文件描述符到client数组中 */
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients\n", stderr); /*如果i== FD_SETSIZE表示连接已满,输出错误信息too many clients */
exit(1);
}
FD_SET(connfd, &allset); /*添加新的文件描述符到allset */
if (connfd > maxfd)
maxfd = connfd; /* 如果connfd > maxfd ,交换connfd 和 maxfd 的值,使maxfd中存放的是最大的文件描述符*/
if (i > maxi)
maxi = i; /*如果i > maxi,交换两个变量里的值,maxi存放的是值最大的数组下标*/
if (--nready == 0)
continue; /* 当nready == 0时,表示没有可读的文件描述符,结束本次循环,继续下一次循环*/
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue; /*如果(sockfd = client[i]) < 0,表示没有连接,结束本次循环,继续下次循环*/
if (FD_ISSET(sockfd, &rset)) { /*检测文件描述符是否在rset集合中*/
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { /*判断客户端连接是否关闭*/
/* connection closed by client */
Close(sockfd); /*如果客户端连接关闭,服务器端也关闭*/
FD_CLR(sockfd, &allset); /*将sockfd的文件描述符从allset中清除*/
client[i] = -1;
} else { /*客户端连接没有关闭*/
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]); /*将buf[j]中的字母小写转换为大写并存入buf[j]中*/
Write(sockfd, buf, n); /*调用write将处理结果发给客户端*/
}
if (--nready == 0)
break; /*当nready==0,没有可读的文件描述符,结束本次循环*/
}
}
}
}
流程简介:
首先调用socekt返回一个文件描述符(listenfd)进行监听SERV_PORT端口。然后调用bind将listenfd 和服务器地址servaddr 绑定在一起,使listenfd这个用于网络通讯的文件描述符监听servaddr所描述的地址(本地IP地址)和端口号(8000)。 listen()声明sockfd处于监听状态,并且最多允许有20个客户端处于连接待状态,如果接收到更多的连接请求就忽略。
然后把listenfd放到 allset集合里。在循环里用select轮询,当集合里有可读的socket时,select返回。select返回后,判断是否是listenfd可读,如果是,那么就有新的客户端连进来,调用accept函数进行接收,accept()返回时传出客户端的地址和端口号。Accept得到的connfd保存到客户端列表里(client[n]),然后同样加到allset集合里,以便进行下一次轮询。如果不是listenfd可读,那么遍历所有客户端socket,检查是否可读,如果可读,就读取数据,转换成大写然后发送回客户端。如果客户端断开,那么从client列表和集合里去除掉。
运行结果:
服务器端:
[root@localhost dyq]# gcc server.c -o server
[root@localhost dyq]# ./server
received from 127.0.0.1 at PORT 60058
received from 127.0.0.1 at PORT 60059
received from 127.0.0.1 at PORT 47114
客户端1:
[root@localhost dyq]# gcc client.c -o client
[root@localhost dyq]# ./client
wo
WO
men
MEN
shi
SHI
yi
YI
jia
JIA
客户端2:
[root@localhost dyq]# ./client
cat
CAT
dog
DOG
bird
BIRD
客户端3:
[root@localhost dyq]# ./client
hello world
HELLO WORLD
阅读(1071) | 评论(0) | 转发(0) |