Linux--高级I/O多路复用之select
来源:互联网 发布:java封装是什么意思 编辑:程序博客网 时间:2024/06/11 06:14
1. select是做什么的?
系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视**多个文件句柄**的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。
2. select函数原型?
![这里写图片描述](http://img.blog.csdn.net/20170606093025905?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2F5aGVsbG9fd29ybGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 这里有几个参数。 1.参数nfds是需要监视的最大的文件描述符值+1。2.参数rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。 如果对某一个的条件不感兴趣,就可以把它设为空指针。struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符,可通过以下四个宏进行设置:
void FD_ZERO(fd_set *fdset); //清空集合 void FD_SET(int fd, fd_set *fdset); //将一个给定的文件描述符加入集合之中 void FD_CLR(int fd, fd_set *fdset); //将一个给定的文件描述符从集合中删除 int FD_ISSET(int fd, fd_set *fdset); // 检查集合中指定的文件描述符是否可以读写
3.timeout告知内核等待所指定描述字中的任何一个就绪可花多少时间。其timeval结构用于指定这段时间的秒数和微秒数。
struct timeval{ long tv_sec; //seconds long tv_usec; //microseconds };
这个参数有三种选项:
(1)永远等待下去:仅在有一个描述字准备好I/O时才返回。为此,把该参数设置为空指针NULL。
(2)等待一段固定时间:在有一个描述字准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微秒数。
(3)根本不等待:检查描述字后立即返回,这称为轮询。为此,该参数必须指向一个timeval结构,而且其中的定时器值必须为0。
select特点:
1、可监控的文件描述符个数取决与sizeof(fd_set)的值。本人服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,服务器上支持的最大文件描述符是4096(512*8)。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
2、将fd加入select监控集的同时,还要使用一个数据结构array保存放到select监控集中的fd。
一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断。
二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
3、select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有时间发生)。
select缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
(3)select支持的文件描述符数量太小了,默认是1024。
测试程序:
server端:
#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>#include<sys/stat.h>#include<fcntl.h>int array_fds[1024];int startup(char *ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0) { perror("socket"); exit(1); } int flg = 1; setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flg,sizeof(flg)); struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){ perror("bind"); exit(3); } if(listen(sock,10)<0){ perror("listen"); exit(4); } return sock;}int main(int argc ,char* argv[]){ if(argc != 3) { printf("usage: %s [ip] [port]\n",argv[0]); return 1; } int maxfd = 0; int listenSock = startup(argv[1],atoi(argv[2])); fd_set rfds; array_fds[0] = listenSock; int array_size = sizeof(array_fds)/sizeof(array_fds[0]); int i = 1; for(;i<array_size;++i) array_fds[i] = -1; while(1) { struct timeval timeout = {0,0}; FD_ZERO(&rfds); maxfd = -1; for(i = 0;i<array_size;++i) { if(array_fds[i] > 0){ FD_SET(array_fds[i],&rfds); if(array_fds[i] > maxfd) maxfd = array_fds[i]; } } switch(select(maxfd+1,&rfds,NULL,NULL,NULL))//这里最后一个参数为timeout 则为等待一段时间后退出,若为NULL则表示阻塞等待 { case 0: printf("timeout...\n"); break; case -1: perror("select"); break; default: { int j = 0; for(;j<array_size;++j) { if(array_fds[j] < 0) continue; //j==0表示listensock响应 if(j == 0 && FD_ISSET(array_fds[j],&rfds)){ struct sockaddr_in client; socklen_t len = sizeof(client); int new_fd = accept(array_fds[j],\ (struct sockaddr*)&client,&len); if(new_fd < 0){ perror("accept"); continue; }else{ printf("get a new client:{%s:%d}\n",\ inet_ntoa(client.sin_addr),\ ntohs(client.sin_port)); int k = 1; for(;k<array_size;++k){ if(array_fds[k] < 0){ array_fds[k] = new_fd; break; } } if(k == array_size){ close(new_fd); } } } //j!=0表示其他的响应 else if(j!=0 && FD_ISSET(array_fds[j],&rfds)){ char buf[10240]; ssize_t s = read(array_fds[j],\ buf,sizeof(buf)-1); if(s > 0){ buf[s] = 0; printf("client say :%s\n",buf); }else if(s == 0){ printf("client quit~\n"); close(array_fds[j]); array_fds[j] = -1; }else{ perror("read"); close(array_fds[j]); array_fds[j] = -1; } } } break; } } }}
实验结果:
- Linux--高级I/O多路复用之select
- Linux--高级I/O多路复用之Poll
- Linux--高级I/O多路复用之epoll
- 【网络】高级I/O多路复用之select、poll和epoll
- I/O多路复用之select
- I/O 多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O多路复用之select
- I/O 多路复用之select
- Linux I/O多路复用之select,poll与epoll区别
- Linux I/O多路复用之select,poll与epoll区别
- Linux网络编程---I/O多路复用 之 select
- Android实用功能分享——应用版本的更新实例
- 算法细节系列(29):any sum
- Rapidjson的简单使用示例
- Linux应用程序性能优化教程
- 栈(stack)的模板类及成员函数的实现
- Linux--高级I/O多路复用之select
- Android常用实例——截取APP当前界面(可带图片、文字水印)
- Angular开发(二十三)-angular响应式表单正确开发姿势
- 基于 RabbitMQ 的实时消息推送 开源 免费 有demo
- LeetCode | 9. Palindrome Number (回文数)
- 这是我的第一篇文章
- Django开发博客-(3)Models
- FATS_POW
- 基于直方图对比度的显著性检测(HC)