select实现I/O复用
来源:互联网 发布:mac电脑激活时间查询 编辑:程序博客网 时间:2024/05/17 00:59
select:
系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的对应的FILE *结构的表示就是stdin、stdout、stderr。
select函数:
#include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数nfds是需要监视的最大的文件描述符值+1;
rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符的集合。
struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位
FD_ZERO(fd_set *set);用来清除描述词组set的全部位
参数timeout为结构timeval,用来设置select()的等待时间,
(1)如果参数timeout设为:
NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了
事件。
(2)0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
(3)特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。
函数返回值:
执行成功则返回文件描述词状态已改变的个数
如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;
当有错误发生时则返回-1,
select实现I/0复用:
tcp_server.c:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/socket.h> 5 #include<arpa/inet.h> 6 #include<netinet/in.h> 7 #include<assert.h> 8 #include<unistd.h> 9 10 int fds[64]; 11 const int back_log=5; 12 void usage(char* argv) 13 { 14 printf("%s:[ip][port]\n",argv); 15 } 16 int start_up(char* ip,int port) 17 { 18 //sock 19 int sock=socket(AF_INET,SOCK_STREAM,0); 20 if(sock<0) 21 { 22 perror("sock"); 23 exit(0); 24 } 25 struct sockaddr_in local; 26 local.sin_port=htons(port); 27 local.sin_family=AF_INET; 28 local.sin_addr.s_addr=inet_addr(ip); 29 30 //bind 31 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 32 { 33 perror("bind"); 34 exit(1); 35 } 36 //listen 37 if(listen(sock,back_log)<0) 38 { 39 perror("sock"); 40 exit(1); 41 } 42 return sock; 43 } 44 int main(int argc,char* argv[]) 45 { 46 if(argc!=3) 47 { 48 usage(argv[0]); 49 exit(1); 50 } 51 int port=atoi(argv[2]); 52 char* ip=argv[1]; 53 54 55 int done=0; 56 int new_sock=-1; 57 int listen_sock=start_up(ip,port); 58 struct sockaddr_in client; 59 socklen_t len=sizeof(client); 60 61 int max_fd; 62 fd_set _reads; 63 fd_set _writes; 64 65 int i=0; 66 int fds_num=sizeof(fds)/sizeof(fds[0]); 67 for(i=0;i<fds_num;i++) 68 { 69 fds[i]=-1; 70 } 71 fds[0]=listen_sock; 72 max_fd=fds[0]; 73 74 while(!done) 75 { 76 FD_ZERO(&_reads); //每次循环把_reads,_writes初始化(输入、输出 参数) 77 FD_ZERO(&_writes); 78 FD_SET(listen_sock,&_reads); //把listen_sock加到_reads文件描 述符集中 79 struct timeval _timeout={5,0}; //设置等待时间 80 for(i=0;i<fds_num;i++) 81 { 82 if(fds[i]>0) 83 { 84 FD_SET(fds[i],&_reads); 85 if(fds[i]>max_fd) 86 { 87 max_fd=fds[i]; 88 } 89 } 90 } 91 switch(select(max_fd+1,&_reads,&_writes,NULL,&_timeout)) //_reads,_writes输入,输出参数 92 { 93 case 0: 94 printf("timeout\n"); 95 break; 96 case -1: 97 perror("select"); 98 break; 99 default:100 {101 for(i=0;i<fds_num;i++)102 {103 if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads)) //listen_sock104 {105 new_sock=accept(listen_sock,(struct sockaddr*)&clien t,&len);106107 if(new_sock<0)108 {109 perror("new_sock");110 continue;111 }112 printf("get connection...%ld\n",new_sock);113 for(i=0;i<fds_num;i++) //把new_sock加到_reads文件描述集114 {115 if(fds[i]==-1)116 {117 fds[i]=new_sock;118 break;119 }120 }121 if(i==fds_num) //文件描述符个数已达到最大值122 {123 close(new_sock);124 }125 }126 127 else if(fds[i]>0&&FD_ISSET(fds[i],&_reads)) //普通的sock,通信128 {129 char buf[1024];130 ssize_t _s=read(fds[i],buf,sizeof(buf)-1);131 if(_s>0)132 {133 buf[_s]='\0';134 printf("%s\n",buf);135 }136 else if(_s==0)137 {138 printf("client closed\n");139 }140 else141 {142 perror("read");143 }144 }145 else146 {147 148 }149 }150 }151 }152 }153 return 0;154 }
tcp_client.c:
1 2 #include<sys/socket.h> 3 #include<sys/types.h> 4 #include<unistd.h> 5 #include<errno.h> 6 #include<string.h> 7 #include<arpa/inet.h> 8 #include<netinet/in.h> 9 #include<string.h> 10 #include<stdlib.h> 11 #include<stdio.h> 12 13 14 void usage(char* proc) 15 { 16 printf("Usage:%s[ip][port]\n",proc); 17 } 18 int main(int argc,char* argv[]) 19 { 20 if(argc!=3) 21 { 22 usage(argv[0]); 23 exit(1); 24 } 25 char* ip=argv[1]; 26 int port=atoi(argv[2]); 27 28 //socket 29 int sock=socket(AF_INET,SOCK_STREAM,0); 30 if(sock<0) 31 { 32 perror("sock"); 33 exit(2); 34 } 35 struct sockaddr_in remote; 36 remote.sin_family=AF_INET; 37 remote.sin_port=htons(port); 38 remote.sin_addr.s_addr=inet_addr(ip); 39 40 int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote)); 41 if(ret<0) 42 { 43 perror("coneect"); 44 } 45 46 char buf[1024]; 47 while(1) 48 { 49 memset(buf,'\0',sizeof(buf)); 50 read(0,buf,sizeof(buf)-1); 51 ssize_t _s= write(sock,buf,sizeof(buf)-1); 52 if(_s<0) 53 { 54 perror("write"); 55 } 56 } 57 return 0; 58 }
结果:
server端:
[admin@www Internet1]$ ./tcp_server 127.0.0.1 8080get connection...4timeoutwe are young^C
client端:
[admin@www Internet1]$ ./tcp_client 127.0.0.1 8080we are young^C[admin@www Internet1]$
select缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
本文出自 “liveyoung” 博客,转载请与作者联系!
- select实现I/O复用
- I/O复用------select
- I/O复用-select
- select实现I/O多路复用
- I/O复用:select()函数
- I/O复用select函数
- select用于实现I/O多路复用
- select用于实现I/O多路复用
- select用于实现I/O多路复用
- I/O复用:Select和Poll函数
- I/O复用----fcntl和select
- (UNP点滴记录) I/O复用select
- I/O复用——select
- i/o复用 select和poll用法
- tcp,select函数支持I/O复用
- 浅谈I/O复用:select、poll、epoll
- I/O复用(一)--select & poll
- Socket编程实践 -- Select I/O复用
- 二叉树的层次遍历
- 栈的压入、弹出序列
- 网络中进程通信-----socket
- 基于UDP协议的socket通信
- 三角形类雏形3
- select实现I/O复用
- I/O多路转接之poll
- tensorflow架构
- Eclipse调试Java的10个技巧
- MySQL服务器上添加一个允许远程访问的用户
- poj 1496 Word Index 计数/组合数学
- android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is yo
- 三角形类雏形4
- 多文件(头文件、源文件、)的结构的编程