server的注释与执行过程
计算机与信息工程学院 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);//这个宏表示可以在本地的所有IP地址上监听,并将32位IP地址从主机字节序转化为网络字节序;
servaddr.sin_port = htons(SERV_PORT);//将(16位)端口号从主机字节序转化为网络字节序;
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//将参数listenfd和servaddr绑定在一起,让用于网络通讯的文件描述符listenfd来监听servaddr所描述的地址和端口号;
Listen(listenfd, 20);//申明listenfd处于监听的状态,并且最多允许20个客户端处于连接状态;
maxfd = listenfd; /* initialize */ 将监听到的最大的文件描述符listenfd保存到maxfd里;
maxi = -1; /* index into client[] array */初始化maxi;
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /* -1 indicates available entry */ 初始化client;
FD_ZERO(&allset);//将allset清零;
FD_SET(listenfd, &allset);//把listenfd添加到allset集合中;
for ( ; ; ) {
rset = allset; /* structure assignment */赋值;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);//在rset这个结构体中的文件是否有可读信息;
if (nready < 0)
perr_exit("select error");//select函数出错;
if (FD_ISSET(listenfd, &rset)) { /* new client connection */ 检查listenfd是否可读;
cliaddr_len = sizeof(cliaddr);//测试cliaddr的长度;
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);//接受客户端的请求,并将客户端的地址和端口号,放到listenfd中返回,并赋值给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; /* save descriptor */
break;
}
if (i == FD_SETSIZE) {
fputs("too many clients ", stderr);//错误;
exit(1);
}
FD_SET(connfd, &allset); /* add new descriptor to set */ 添加connfd到allset集合中
if (connfd > maxfd)//比较新添加的connfd与maxfd;
maxfd = connfd; /* for select */把最大编号的fd放到maxfd中;
if (i > maxi)
maxi = i; /* max index in client[] array */
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文件是否可读;
{
if ( (n = Read(sockfd, buf, MAXLINE)) == 0)//从sockfd所知文件中读MAXLINE个字节到buf中;
{
/* connection closed by client */
Close(sockfd);//没有这client关闭连接;
FD_CLR(sockfd, &allset);//清空allset集合;
client[i] = -1;//没有客户端请求;
} else {
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);//将buf中的小写转化为大写;
Write(sockfd, buf, n);// 将处理结果发送给客户端;
}
if (--nready == 0)//再次判断是否有可读取的数据;
break; /* no more readable descriptors */
}
}
}
}
程序的执行过程:
调用socket()打开一个地址类型为IPV4,传输协议为TCP的网络通讯端口,将返回值赋给listenfd。
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
调用以 上四句对band()中的server进行初始化,首先将结构体server清零,设置地址类型为AF_INET,网络地址为INADDR_ANY,端口 号我们定义为8000。调用bind()将参数sockfd和刚才初始化的server绑定在一起。然后调用listen()声明listenfd处于监 听状态,并且规定最多允许20个客户端处于连接状态,如果接到更多的连接请求就忽略。把监听到fd放到maxfd中。定义maxi,使用for()循环, 对client进行初始化为-1,表示没有客户端连接。然后先清空allset集合,然后再把刚才监听到的listenfd文件描述符放到allset这 个文件描述符集合中。
在for()死循环中,首先将allset集合中的数据赋值给rset,然后调用select()判断在rset这个集合中是否有可以从这些所包含的文件中读取数据。客户端有几个请求则返回的数是几。并将这个数保存到nready中。If是判断若是调用select()出错,打印出提示信息。
此时select()的返回值此时nready=1大于零,所以执行If判断listenfd在tset中,accept()就开始执行,接受一个新的客户端请求,将新的客户端的地址和端口号放在新生成一个文件描述符中并将这个文件描述符赋给connfd。
然后调用printf()打印出客户端的发送连接请求的服务器的地址并把客户端的端口打印出来。执行for循环,此时client[0]=-1, 把新接受的connfd放到client这个数组中。终止此次的for循环。然后将connfd放到allset这个集合中,并将此时最大的文件描述符放 到maxfd中,这时allset中包含listenfd和connfd,i中存放的是最大的文件描述符,把i赋给maxi。
把allset集合里 的新添加的connfd放到,rset集合中,假设现在这两个文件中都有数据可读取,当执行select()时nready=2,for()循环中前面的 执行同上分析,--nready!=0,所以执行下一个for(),进入for语句把connfd赋给sockfd,因为connfd有连接请求并判断客 户端没有关闭连接,接收来自客户端的数据并将小写装换为大写重新返回给客户端;如果客户端关闭连接,服务器端也关闭连接,并从allset清除 sockfd的文件描述符.把ready的值自减1,nready为0,没有更多的文件描述符,结束本次循环.进入下次for循环,重新监听来自客户端的 连接请求.
实验执行结果:
服务器端:
[root@localhost wodechengxu]# gcc server.c -o server.o
[root@localhost wodechengxu]# ./server.o
received from 127.0.0.1 at PORT 38509
received from 127.0.0.1 at PORT 45203
c客户端1:
[root@localhost wodechengxu]# gcc client.c -o client.o
[root@localhost wodechengxu]# ./client.o
wodemingzi
WODEMINGZI
liu
LIU
xuexi
XUEXI
客户端2:
[root@localhost wodechengxu]# ./client.o
fffffff
FFFFFFF
gggggg
GGGGGG
关闭服务器端后的客户端:
[root@localhost wodechengxu]# ./client.o
connect error: Connection refused