IO复用之select
select使用的是IO复用的方式进行多客户端的处理,当有客户端的连接以后会将该客户端的套接字等信息添加到队列中,然后采用轮询的方式来判断客户端是否有消息发送过来.如果客户端断开,重置队列信息,关闭套接字.
因为是使用轮询的方式进行,所以在较多客户端下速度会较慢,而且受到Linux文件描述符的数量的限制,建议在中低并发量中使用.
同样的select也有一般的形式:
int main(int argc,char *argv[])
{
int listenfd; //监听套接字
fd_set current_fds,bak_fds; //select的队列
int ret; //判断返回值是否正确
int maxfds; //最大连接数
int i; //用于轮询的循环计数
int fdtmp; //当前数值最大的套接字,用来减少轮询的数量
// 创建监听描述符
listenfd = create_tcp_server(8888);
if (listenfd < 0) {
perror("create tcp server failed!");
exit(-1);
}
/*初始化队列*/
FD_ZERO(&bak_fds); //初始化队列
FD_SET(0,&bak_fds); //初始化队列
FD_SET(listenfd,&bak_fds); //将监听套接字放到队列中
maxfds = listenfd; //将最大的套接字修改为监听套接字
//死循环来等待和处理客户端
while (1){
current_fds = bak_fds;//保险起见将队列复制一份使用
ret = select(maxfds+1, ¤t_fds, NULL, NULL, NULL);//启动select函数
if (ret < 0) {//判断是否成功
perror("select:");
close(listenfd);
return -1;
}
for (i = 0; i <= maxfds; i++) {//轮询
if( FD_ISSET(i,¤t_fds) ){//读取相关的描述符
if (i == 0) {//标准输入输出
continue;
}
else if( i == listenfd ){//有新连接
fdtmp = process_listend(listenfd);//处理新连接
if (fdtmp < 0) {//出错
fprintf(stderr, "listenfd failed!\n");
break;
}
printf("the newfd is %d\n",fdtmp);
FD_SET(fdtmp,&bak_fds);//将新的客户端的描述符加入队列
if (fdtmp > maxfds) {//更新最大的描述符
maxfds = fdtmp;
}
}
else{//有信息发送过来
ret = process_message(i);//处理发送过来的信息
if (ret == 0) {//客户端断开连接
FD_CLR(i,&bak_fds);
close(i);
}
}
}
}
}
}
int process_listend(int sockfd)
{
int newfd;
newfd = accept(sockfd, NULL, NULL);
if (newfd < 0) {
perror("accept");
return -1;
}
printf("have a new connection...\n");
return newfd;
}