班级:09计算机应用技术1班 姓名:赵超越 学号:0906041011
/* server.c */
#include
#include
#include
#include
#include "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];
ssize_t n;
fd_set rset, allset; /*定义fd_set(文件描述符的集合)型变量 rset allset*/
char buf[MAXLINE]; /*定义缓冲区数组buf[ ]*/
char str[INET_ADDRSTRLEN]; /*定义字符数组str[ ]*/
socklen_t cliaddr_len;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0); /*打开一个网络通讯端口,地址类型IPV4,TCP协议*/
bzero(&servaddr, sizeof(servaddr)); /*servaddr 初始化*/
servaddr.sin_family = AF_INET; /*设置地址类型为 IPV4*/
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*设置网络地址为本地任意IP地址*/
servaddr.sin_port = htons(SERV_PORT); /*定义端口号 SERV_PORT(8000)*/
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); /*调用bind将参数listenfd与servaddr绑定,使listenfd监听servaddr所描述的地址和端口*/
Listen(listenfd, 20); /*listen()声明listenfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,0—成功,-1—失败*/
maxfd = listenfd; /*将maxfd初始化*/
maxi = -1; /*将maxi赋初值为-1*/
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /*将client[i]中的每一个元素赋初值为-1*/
FD_ZERO(&allset); /* 将文件描述符集合allset置零*/
FD_SET(listenfd, &allset); /*将监听文件描述符listenfd放入集合allset中*/
for ( ; ; ) { /*死循环*/
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);/* 调用select函数它可以同时监听多个阻塞的文件描述符,并且处理有数据到达的文件描述符,并返回更新的个数给nready,没有数据到达就阻塞*/
if (nready < 0)
perr_exit("select error"); /*当nready小于0时出错* /
if (FD_ISSET(listenfd, &rset)) { /*检测文件描述符listenfd是否在rset集合中(检测rset中是否有新的连接出现)/*
cliaddr_len = sizeof(cliaddr); /*对cliaddr_len赋初值,并将本地客户端地址的长度放入变量cliaddr_len中*/
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); /*服务器调用accept()接受连接,若没有客户端的连接请求,就阻塞等待直到有客户端连接上来;若有客户端连接则解除阻塞状态,并返回一个服务器与客户端互相通信的文件描述符connfd*/
printf("received from %s at PORT %d
",
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[i]中*/
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients
", stderr);/*如果i == FD_SETSIZE表示连接已满,则输出错误信息" too many clients "*/
exit(1); /*异常退出*/
}
FD_SET(connfd, &allset); /*将新的文件描述符connfdt添加到allset中 */
if (connfd > maxfd)
maxfd = connfd; /*如果connfd > maxfd,则将connfd的值赋给maxfd,使得maxfd存放的是最大的文件描述符*/
if (i > maxi)
maxi = i; /*如果i > maxi,将i的值赋给maxi,使得maxi存放的是值最大的i*/
if (--nready == 0)
continue; /*当nready为0,没有可读的文件描述符,则结束本次循环。并开始执行下一次循环*/
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)
continue; /*如果没有连接,则结束本次循环,并开始执行下一次循环*/
if (FD_ISSET(sockfd, &rset)) /*检测文件描述符listenfd是否在rset集中*/
{
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) /*判断客户端连接是否关闭*/
{
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()函数判断rset 中存放的listenfd 是否有数据到达,现在有一个文件描述符listenfd 在rset 中,假设有数据到达返回1赋给nready , 判断 nready 不小于0,所以不出错。接着服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接 上来。当集合里有可读的socket时,select返回。select返回后,判断是否是listenfd可读,如果为真,则说明有新的客户端连接进来,然后调用accept函数进行接收,accept()返回时返回一个服务器与客户端互相通信的文件描述符connfd,存放传出客户端的地址和端口号并打印。Accept得到的connfd保存到客户端列表里(client[i] 数组中的第一个位置), 通过if 语句判断i 不等于FD_SETSIZE退出。把最大的描述符添加到allset集中,把最大的i 值赋给maxi , nready = 1继续返回执行for循环,重新等待监听来自客户端的连接请求。 把client[i] 中第一个元素(即第一个文件描述符listenfd )赋给sockfd 不小于0执行下面的if 语句,判断sockfd(listenfd )在rset 集中,接着判断客户端是否关闭连接。若已经关闭连接,将sockfd(listenfd )从allset 集中清除,将client[i]的第一个元素赋值为-1。若没有关闭连接,将接收到的来自客户端的数据由小写转换为大写,重新返回给客户端。果客户端断开,那么从client列表和集合里去除掉。连接关闭后,此时nready = 1执行“——”操作后后等于0,没有更多的文件描述符,结束本次循环,进入下一次for循环,重新监听来自客户端的连接请求。
执行结果:
终端1:
[root@localhost ~]#cd /opt/zcy/
[root@localhost zcy]#gcc server.c -o server
[root@localhost zcy]#./server
received from 127.0.0.1 at PORT 50275
received from 127.0.0.1 at PORT 50276
终端2:
[root@localhost zcy]# ./client
zcy
ZCY
nypd
NYPD
www
WWW
终端3:
[root@localhost ~]# cd /opt/zcy/
[root@localhost zcy]# ./client
zhongduan3
ZHONGDUAN3