【Linux】多路复用之—select
2016-05-20 21:21
531 查看
一:多路复用之——select
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout)
1、参数: (1)nfds:需要监视的文件描述符数目;
(2)readfds、writefds、ecceptfds:对应于需要检测的可读文件描述符集合、可写文件描述符集合、异常文件描述符集合;
(3)timeout:NULL:没有timeout,一直阻塞,直到某个文件描述符发生事件
0:仅检测文件描述符的状态,然后立即返回
特定值:等待timeout时间,如果事件没有发生,超时返回
2、宏:提供处理这三种描述符词组的方式
(1):FD_CLR(int fd,fd_set* set)清除文件描述符集中相关fd的位
(2):FD_ISSET(int fd,fd_set* set)测试文件描述符集中与fd相关的事件是否发生
(3):FD_SET(int fd,,fd_set* set)设置文件描述符集中相关fd的位
(4):FD_ZERO(int fd,fd_set* set)清除文件描述符集中的全部位
3、timeout:
struct timeout{
long tv_sec 秒
long tv_usec 微妙
}
4、返回值:
(1)成功:返回文件描述符集中状态改变的文件描述符个数
(2)0:超过timeout时间
(3)-1:失败
5、理解select模型:
取fd_set长度为1字节,fd_set中的每一bit位对应一个文件描述符fd,则1字节长的fd_set最多可以对应8个fd
(1)执行fd_set set,DF_ZERO(&set),则set用位表示为0000 0000
(2)若fd=5,执行fd_set(fd,&set),后set变为0001 0000
(3)若再加入fd=1,fd=2,则set变为0001 0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2都发生,则select返回,set变为0000 0011 注意:没有事件发生的fd=5被清空
6、select模型的特点:
(1)可监控的文件描述符个数取决于sizeof(fd_set)的值;
(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判断是否有事件发生)。
7、select模型的缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,当fd很多时,开销会很大;
(2)在每次调用select时,都要遍历fd,开销大;
(3)select支持的文件描述符数目太少,默认是1024,不能处理海量数据。
my_select.c
client.c
Makefile
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout)
1、参数: (1)nfds:需要监视的文件描述符数目;
(2)readfds、writefds、ecceptfds:对应于需要检测的可读文件描述符集合、可写文件描述符集合、异常文件描述符集合;
(3)timeout:NULL:没有timeout,一直阻塞,直到某个文件描述符发生事件
0:仅检测文件描述符的状态,然后立即返回
特定值:等待timeout时间,如果事件没有发生,超时返回
2、宏:提供处理这三种描述符词组的方式
(1):FD_CLR(int fd,fd_set* set)清除文件描述符集中相关fd的位
(2):FD_ISSET(int fd,fd_set* set)测试文件描述符集中与fd相关的事件是否发生
(3):FD_SET(int fd,,fd_set* set)设置文件描述符集中相关fd的位
(4):FD_ZERO(int fd,fd_set* set)清除文件描述符集中的全部位
3、timeout:
struct timeout{
long tv_sec 秒
long tv_usec 微妙
}
4、返回值:
(1)成功:返回文件描述符集中状态改变的文件描述符个数
(2)0:超过timeout时间
(3)-1:失败
5、理解select模型:
取fd_set长度为1字节,fd_set中的每一bit位对应一个文件描述符fd,则1字节长的fd_set最多可以对应8个fd
(1)执行fd_set set,DF_ZERO(&set),则set用位表示为0000 0000
(2)若fd=5,执行fd_set(fd,&set),后set变为0001 0000
(3)若再加入fd=1,fd=2,则set变为0001 0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2都发生,则select返回,set变为0000 0011 注意:没有事件发生的fd=5被清空
6、select模型的特点:
(1)可监控的文件描述符个数取决于sizeof(fd_set)的值;
(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判断是否有事件发生)。
7、select模型的缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,当fd很多时,开销会很大;
(2)在每次调用select时,都要遍历fd,开销大;
(3)select支持的文件描述符数目太少,默认是1024,不能处理海量数据。
my_select.c
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<arpa/inet.h> #include<netinet/in.h> #include<sys/socket.h> #include<sys/types.h> #include<sys/select.h> #define _PORT_ 8080 #define _BACK_LOG_ 5 #define _MAX_FD_NUM_ 32 int array_fd[_MAX_FD_NUM_]; int startup() { int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0){ perror("socket"); exit(1); } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(_PORT_); local.sin_addr.s_addr=htonl(INADDR_ANY); socklen_t len=sizeof(local); if(bind(sock,(struct sockaddr*)&local,len)<0){ perror("bind"); exit(2); } if(listen(sock,_BACK_LOG_)<0){ perror("listen"); exit(3); } return sock; } int main() { int listen_sock=startup(); struct sockaddr_in client; socklen_t len=sizeof(client); fd_set read_set; int i=0; int max_fd=listen_sock; array_fd[0]=listen_sock; for(i=1;i<_MAX_FD_NUM_;i++){ array_fd[i]=-1; } while(1){ FD_ZERO(&read_set); for(i=0;i<_MAX_FD_NUM_;i++){ if(array_fd[i]>0){ FD_SET(array_fd[i],&read_set); if(max_fd<array_fd[i]){ max_fd=array_fd[i]; } } } struct timeval time_out={3,0}; switch(select(max_fd+1,&read_set,NULL,NULL,&time_out)){ case 0://timeout printf("timeout...\n"); break; case -1://error perror("select"); break; default: { for(i=0;i<_MAX_FD_NUM_;i++){ if(array_fd[i]<0){ continue; }else if(array_fd[i]==listen_sock && FD_ISSET(array_fd[i],&read_set)){ int new_sock=accept(array_fd[i],(struct sockaddr*)&client,&len); if(new_sock<0){ continue; } printf("get a new connect...\n"); for(i=0;i<_MAX_FD_NUM_;i++){ if(array_fd[i]==-1){ array_fd[i]=new_sock; break; } } if(i==_MAX_FD_NUM_){ printf("array_fd is full\n"); close(new_sock); } }else{ for(i=1;i<_MAX_FD_NUM_;i++){ if(array_fd[i]>0 && FD_ISSET(array_fd[i],&read_set)){ char buf[1024]; memset(buf,'\0',sizeof(buf)-1); ssize_t _size=read(array_fd[i],buf,sizeof(buf)-1); if(_size==0){ printf("client close...\n"); close(array_fd[i]); array_fd[i]=-1; }else if(_size<0){ }else{ printf("client: %s\n",buf); } } } } } } break; } } close(listen_sock); return 0; }
client.c
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<arpa/inet.h> #include<netinet/in.h> #include<unistd.h> #define _PORT_ 8080 int main() { int read_fd=0; int write_fd=1; fd_set read_set; fd_set write_set; int max_fd=0; int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0){ perror("socket"); exit(1); } struct sockaddr_in remote; remote.sin_family=AF_INET; remote.sin_port=htons(_PORT_); remote.sin_addr.s_addr=inet_addr("192.168.0.146"); if(connect(sock,(struct sockaddr*)&remote,sizeof(remote))<0){ perror("connect"); exit(2); } if(sock>read_fd){ max_fd=sock; }else{ max_fd=read_fd; } while(1){ FD_ZERO(&read_set); FD_ZERO(&write_set); FD_SET(read_fd,&read_set); FD_SET(sock,&write_set); switch(select(max_fd+1,&read_set,&write_set,NULL,NULL)){ case 0: printf("timeout...\n"); break; case -1: perror("select"); break; default: { if(FD_ISSET(read_fd,&read_set)){ char buf[1024]; memset(buf,'\0',sizeof(buf)); ssize_t _size=read(read_fd,buf,sizeof(buf)-1); if(_size>0){ buf[_size]='\0'; printf("echo: %s\n",buf); } if(FD_ISSET(sock,&write_set)){ send(sock,buf,strlen(buf),0); } } } break; } } return 0; }
Makefile
.PHONY:all all:my_select client my_select:my_select.c gcc -o $@ $^ client:client.c gcc -o $@ $^ .PHONY:clean clean: rm -rf my_select client
相关文章推荐
- Linux is not Matrix——日志搜集平台ELK(II)
- Linux下的进程通信方式: 管道通信详解
- 在linux下安装android模拟器
- linux文件的三种时间
- Linux 命令行模式快捷键使用
- CentOS(5.8/6.4)linux生产环境若干优化实战
- 坑爹的一天——关于配置samba服务器(1)
- Linux源码环境下编译apk实践
- 从开始到结束,手把手教你使用busybox构建类嵌入式Linux系统
- Linux_2_Navigation
- Linux内核Makefile分析
- Linux文件权限
- selinux禁用后系统无法正常启动的问题
- Linux的lvm管理:修改LV大小
- 《Linux及安全》实践2
- Linux中常见find命令的使用
- CentOS 7安装Nexus,搭建Maven私服
- centos mysql自动备份脚本
- linux 学习资料
- linux locale