班级:09计应用二班 姓名:郑明莉 学号:0906042007
/* 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); //打开一个IPV4的TCP连接端口
bzero(&servaddr, sizeof(servaddr));//清空服务器的地址
servaddr.sin_family = AF_INET;//服务器的地址类型为IPV4
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//服务器允许任何客户端访问
servaddr.sin_port= htons(SERV_PORT);//服务器端口设为8000
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//把监听端口和服务器通信端口绑定
Listen(listenfd, 20);开始监听服务器通信端口
maxfd = listenfd; /* initialize */把最新的连接描述符赋值给maxfd,三次握手第二步;
maxi = -1; /* index into client[] array */maxi初始化为-1;
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */初始化连接空间
FD_ZERO(&allset);把allset清空
FD_SET(listenfd, &allset);把listenfd描述符添加进allset文件描述符集
for ( ; ; ) {
rset = allset; /* structure assignment */把连接赋值给rset
nready = select(maxfd+1, &rset, NULL, NULL, NULL);把已连接的描述符保存在nready里;
if (nready < 0)
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* new client connection */如果listenfd已经在rset集里,则执行此命令下面的语句;
cliaddr_len = sizeof(cliaddr);客户端地址长度
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);把新的描述符给connfd,接受listenfd连接,三次握手完成;
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; /* save descriptor */把已连接的客户端文件描述符保存在client 数组里
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients ", stderr);如果连接已满则输出错误信息
exit(1);错误输出
}
FD_SET(connfd, &allset); /* add new descriptor to set */将新的文件描述符添加进allset 里!
if (connfd > maxfd)
maxfd = connfd; /* for select */把最新的文件描述符保存在maxfd里
if (i > maxi)
maxi = i; /* max index in client[] array */把已有的连接总数保存在maxi里
if (--nready == 0)
continue; /* no more readable descriptors */如果没有文件描述符可读则结束此次循环
}
for (i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i]) < 0)如果没连接则结束此次循环
continue;
if (FD_ISSET(sockfd, &rset)) {如果sockfd在rset里则执行下面的语句
if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {如果没有读到内容则执行下面语句
/* connection closed by client */
Close(sockfd);关闭通信端口
FD_CLR(sockfd, &allset);从allset中清除sockfd这个端口文件描述符
client[i] = -1;将这个空间设置为没有连接
} else {如果读到内容则将服务器接受来自客户端的数据,返回给客户端
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);将读到的数据转换为大写
Write(sockfd, buf, n);把转换为大写的内容写进buf通道里,写回客户端
}
if (--nready == 0)如果没有客户写入数据则结束这个循环体
break; /* no more readable descriptors */
}
}
}
}
执行过程:
Listenfd=socket(AF_INET,SOCK_STREAM,0)从此句开始,
调用函数Socket(),打开一个IPV4的TCP连接端口,返回一套接字文件描述符赋值给监听套接字listenfd.再把servaddr清空,IPV4的地址类型设置为AF_INET,服务器允许任何客户端访问,NADDR-ANY为本地的任意IP地址,服务器端口设为8000;
调 用Bind函数,将本地监听端口与服务器通信端口绑定在一起,在服务器端调用Listen函数来监听客户端状态。将监听到的文件描述符Listenfd赋 值给maxfd,maxi赋值为-1,表示客户端没有连接,client[]进行初始化,把allset清空,再把监听到的文件描述符 listenfd 添加到文件描述符集allset中;用for循环把allset里的文件描述符给rset,这时allset里有一个listenfd文件描述符. 调 用select函数,如果客户端有连接请求时,将客户端请求个数返回给nready,若有一个请求时,则nready=1,用if判断nready不小于 0,则不出错,这时rset文件描述符集合中有listenfd文件描述符,执行Accept()函数,相当于调用一个socket,生成新的套接字把新 套接字描述符给connfd,再打印出客户端的IP地址,端口号,用子循环for,从低位开始查找空位,再赋值放connfd文件描述符,就是把新得到的文件描述符connfd赋给数组client[],再把新得到的connfd写入 allset文件描述符集合中,此时allset文件描述符集中有listenfd和connfd两个,用if 把allset中的文件描述符和I 最大值找出来分别赋给mxfd ,maxi.—nready==0时,没有更多的文件描述符,结束本次循环,这是就只有一个文件描述符listenfd,因此nready的值为1,--nready时不等0,就不执行continue,进入for 语句把connfd赋给sockfd,这时等于0,结束本次循环.再次执行for循环,监听来自客户端的连接请求. Rset和allset中都有两个文件描述符listenfd,connfd,如果connfd有数据到达,listenfd没有数据到达时,调用select,得到nready 的值 是1,在for 循环中把 connfd给sockfd,如 果客户端和服务器关闭连接,从allset清除sockfd文件描述符,把nready的值自减1,当nready的值为0时,结束本次循环.在此 时,rset ,allset中有 listenfd,connfd,,如果都有连接请求,select返回2,nready为2,if判断出nread的值不小于0, 正确,listendfd在rset 中,执行Accept函数.打印客户端IP地址和端口号,新得到的connfd1保存到client[]
再写入allset 中,这时有listend,connfd,connfd1三个文件描述符,找出最大值给maxfd,maxi,--nready为1,直接进入for把connfd给sockfd,接收来自客户端的数据,把小写转换成大写返回给客户端.
执行结果:
服务器端、
[root@localhost zml]# gcc server.c -o server
[root@localhost zml]# ./server
received from 127.0.0.1 at PORT 50121
received from 127.0.0.1 at PORT 50124
终端一、
[root@localhost zml]# gcc client.c -o client
[root@localhost zml]# ./client
abc
ABC
zhengmingli
ZHENGMINGLI
qianrushibanji
终端二、
[root@localhost zml]# ./client
abc
ABC
zhengmingli
ZHENGMINGLI
qianrushibanji
终端三、
[root@localhost zml]# ./client
qianrushibanji
QIANRUSHIBANJI