select(09嵌入式-贾文龙-注释
/* server.c */
#include
#include
#include
#include
#include "wrap.c"
#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];//宏定义INET_ADDRSTRLEN用于保存网络二进制地址转换为点分十进制地址
socklen_t cliaddr_len;
struct sockaddr_in cliaddr, servaddr;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);//打开一个网络通讯端口
bzero(&servaddr, sizeof(servaddr));//初始化 servaddr 将整个结构体清零
servaddr.sin_family = AF_INET;//设置地址类型为AF_INET
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//网络地址为INADDR_ANY
servaddr.sin_port = htons(SERV_PORT);//端口号
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//将参数listenfd和servaddr绑定在一起,使listenfd这个用于网络通讯的文件描述符监听servaddr所描述的地址和端口号
Listen(listenfd, 20);
maxfd = listenfd; /* 初始化maxfd作为select的第一个参数--最大的文件描述符加一*/
maxi = -1; /* index into client[] array */
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1; /*初始化client[]为 -1 : 指示可获得的输入项*/
FD_ZERO(&allset); //清空集合
FD_SET(listenfd, &allset); //将listenfd文件描述符加入集合之中
for ( ; ; )
{
rset = allset; /* structure assignment */
nready = select(maxfd+1, &rset, NULL, NULL, NULL);//确定rset中套接字的状态
if (nready < 0)
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) //判断 listenfd 是否在rset中
{ /* new client connection */
cliaddr_len = sizeof(cliaddr);
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));
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\n", stderr);
exit(1);
}
FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
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))
{
if ( (n = Read(sockfd, buf, MAXLINE)) == 0)
{
/* connection closed by client */
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
} else
{
int j;
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);
Write(sockfd, buf, n);
}
if (--nready == 0)
break; /* no more readable descriptors */
}
}
}
}
执行过程:
服务器先运行,定义变量并初始化一些变量,for循环,把allset的值赋值给rset,调用select()等待客户端连接,如果有客户端运行,select()返回,判断nready如果小于0就说明出错 退出,否则就判断lisentfd是否在rset里面,在就接受连接,用connfd 代替listenfd 与客户端通信。输出 客户端ip 和端口 ,然后保存 新建的描述符,在判断是否有太多的客户端 若太多 退出。 否则 把connfd 保存进allset 然后在把最大的 文件描述符赋值给maxfd.如果此时 --nready 等于0 那么 就再次循环把allset 的值赋值给rset 此时 select()已经有了一个套接字对象处于满足条件的状态。判断 lisentfd是否在rset里面 此时 rset被allset赋值 只有connfd 不再执行if后面的语句。
执行for循环把上一面新建的描述符赋值给sockfd 并判断这个描述符是否小于0 ,如果小于0说明 client[i]里面没有新的文件描述符,就再次回到第一个for循环处执行。否则就判断 sockfd 在不在rset中,如果在,在判断能不能从客户端读取数据,如果能就执行else下面的语句 并把处理过得数据传回客户端,如果不能 说明客户端一关闭连接,就把新建的文件描述符关闭,并从allset中删除,把client[i]赋值为-1。接着判断--nready是否等于0 如果等于0 就退出本次循环,否则在次循环,判断另一个客户端(client[i])是否有输入有输入就处理并返回客户端,没有就关闭另一个客户端描述符。循环结束时,在执行上一个循环 ,重新接受新的客户端请求。一直循环。
阅读(2246) | 评论(0) | 转发(0) |