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_sock
104 {
105 new_sock=accept(listen_sock,(struct sockaddr*)&clien t,&len);
106
107 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 else
141 {
142 perror("read");
143 }
144 }
145 else
146 {
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 8080
get connection...4
timeout
we are young
^C
client端:
[admin@www Internet1]$ ./tcp_client 127.0.0.1 8080
we are young
^C
[admin@www Internet1]$
select缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
转载于:https://blog.51cto.com/youngyoungla/1783663