标签:select
一.概念
select系统调用是用来让程序监视多个文件句柄的状态变化。程序会停在select这里等待,直到被监视的文件句柄有一个或多个的状态发生了改变。关于文件句柄,其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,3是标准错误。0、1、2是整数表示的,对应的FILE*结构的表示是stdin、stdout、stderr。
参数说明:
(1)nfds:需要监视的最大文件描述符+1;
(2)rdset、wrset、exset:分别对应需要检测的可读文件描述符的集合,可写文件描述符的集合,异常文件描述符的集合。
(3)struct timeval:这个结构用于描述一段时间长度。如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。
(4)处理三组描述词组的方式
void FD_CLR(int fd,fd_set* set); 用来清除描述词组set中相关fd的位
int FD_ISSET(int fd,fd_set* set); 用来测试描述词组set中相关fd的位是否为真
void FD_SET(int fd,fd_set* set); 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set* set); 用来清除描述词组set中所有的位
(5)timeout:为结构timeval,用来设置select的等待时间。其结构如下:
a、当timeout参数设置为NULL:表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。
b、当timeout参数设置为0:只检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
c、当timeout参数设置为特定的时间值:如果在指定的时间段内没有事件发生,则select将超时返回。
三、select函数的返回值分析
(1)执行成功:返回文件描述词状态已改变的个数。
(2)返回0:在描述词状态改变前已超过timeout时间,没有返回。
(3) 返回-1:表示错误。错误原因存于errno中,此时的参数readfds,writefds,exceptfds,timeout的值都讲变成不可预测的。错误值可能为:
EBADF:文件描述词为无效的或者文件已关闭。
EINTR:此调用该信号被中断。
EINVAL:参数n为负值。
ENOMEM:核心内存不足。
四、对select模型的理解
取fd_set的长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set;FD_ZERO(&set):则set用位表示是0000 0000。
(2)若fd=5,执行FD_SET(fd,&set):set变为0001 0000。
(3)若再加入fd=2,fd=1,则set变为0001 0011。
(4)执行select(6,&set,0,0,0)阻塞等待。
(5)若fd=2,fd=1都发生可读事件,则select返回,此时select变为0000 0011。注意:没有发生事件的fd=5被清空。
基于上面的说法,可以得出selec模型的特点。
(1)可监控的文件描述符个数取决于sizeof(fd_set)的值。
(2)强fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。一是用于在select返回后,array作为源数据和fd_set进行FD_ISSET判断。而是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前 都要重新从array取得fd逐一加入。扫描array的同时取得最大值maxfd,用于select的第一个参数。
(3)select模型必须在select前循环array(加fd,取maxfd),select返回后循环array(FD_ISSET判断是否有事件发生)。
五、代码描述
server.c
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<time.h>
6 #include<sys/types.h>
7 #include<sys/socket.h>
8 #include<netinet/in.h>
9 #include<arpa/inet.h>
10
11 #define _BACKLOG_ 5
12
13 void Usage(const char *proc)
14 {
15 printf("%s[ip][port]\n",proc);
16 }
17
18 int start(char *_ip,int _port)
19 {
20 int sock=socket(AF_INET,SOCK_STREAM,0);
21 if(sock<0)
22 {
23 perror("sock");
24 return 1;
25 }
26 struct sockaddr_in local;
27 local.sin_family=AF_INET;
28 local.sin_port=htons(_port);
29 local.sin_addr.s_addr=inet_addr(_ip);
30 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
31 {
32 perror("bind");
33 return 2;
34 }
35 if(listen(sock,_BACKLOG_)<0)
36 {
37 perror("listen");
38 return 3;
39 }
40 return sock;
41 }
42
43 int main(int argc,char *argv[])
44 {
45 if(argc!=3)
46 {
47 Usage(argv[0]);
48 //return 1;
49 }
50 char *ip=argv[1];
51 int port=atoi(argv[2]);
52 int listen_sock=start(ip,port);
53
54 struct sockaddr_in cli;
55 socklen_t len=sizeof(cli);
56 fd_set _reads;
57 fd_set _writes;
58 int fds[64]={0};
59 int fd_max=0;
60 int fds_num=sizeof(fds)/sizeof(fds[0]);
61 int i=0;
62 for(i=0;i<fds_num;++i)
63 {
64 fds[i]=-1;
65 }
66 fds[0]=listen_sock;
67 fd_max=fds[0];
68 struct timeval timeout={5,0};
69 int done=0;
70 int new_sock=-1;
71 while(!done)
72 {
73 FD_ZERO(&_reads);
74 FD_ZERO(&_writes);
75 FD_SET(listen_sock,&_reads);
76
77 timeout.tv_sec=5;
78 timeout.tv_usec=0;
79
80 for(i=1;i<fds_num;++i)
81 {
82 if(fds[i]>0&&i<32)
83 {
84 FD_SET(fds[i],&_reads);
85 if(fd_max<fds[i])
86 {
87 fd_max=fds[i];
88 }
89 }
90 }
91 switch(select(fd_max+1,&_reads,&_writes,NULL,NULL)) 92 {
93 case 0: //timeout
94 {
95 printf("timeout...");
96 break;
97 }
98 case -1: //error
99 {
100 perror("select");
101 break;
102 }
103 default:
104 {
105 for(i=0;i<fds_num;++i)
106 {
107 if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads))
108 {//listen sock is ready
109 new_sock=accept(listen_sock,(struct sockaddr*)&cli,& len);
110 if(new_sock<0)
111 {
112 perror("accept"); 113 continue;
114 }
115 printf("get a new connect:%d\n",new_sock);
116 for(i=0;i<fds_num;++i)
117 {
118 if(fds[i]==-1)
119 {
120 fds[i]=new_sock;
121 break;
122 }
123 }
124 if(i==fds_num)
125 {
126 close(new_sock);
127 }
128 }
129 else if(fds[i]>0&&FD_ISSET(fds[i],&_reads))
130 {//ready is success
131 char buf[1024];
132 memset(buf,‘\0‘,sizeof(buf));
133 ssize_t _size=read(fds[i],buf,sizeof(buf)-1);
134 if(_size<0)
135 {
136 perror("read");
137 close(fds[i]);
138 fds[i]=-1;
139 }
140 else if(_size==0)
141 {
142 printf("client close...");
143 close(fds[i]);
144 }
145 else
146 {
147 FD_CLR(fds[i],&_reads);
148 buf[_size]=‘\0‘;
149 printf("client say:%s",buf);
150 write(fds[i],buf,sizeof(buf)-1);
151 fflush(stdout);
152 }
153 }
154 else
155 {}
156 }
157 }
158 break;
159
160 }
161 }
162 return 0;
163 }client.c
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9
10 void Usage(const char* proc)
11 {
12 printf("%s [ip] [port]\n",proc);
13 }
14
15 int main(int argc,char* argv[])
16 {
17 if(argc!=3)
18 {
19 Usage(argv[0]);
20 exit(1);
21 }
22 int port=atoi(argv[2]);
23 char* ip=argv[1];
24 int client_sock=socket(AF_INET,SOCK_STREAM,0);
25 if(client_sock<0)
26 {
27 perror("socket");
28 exit(2);
29 }
30
31 struct sockaddr_in remote;
32 remote.sin_family=AF_INET;
33 remote.sin_port=htons(port);
34 remote.sin_addr.s_addr=inet_addr(ip);
35 if(connect(client_sock,(struct sockaddr*)&remote,sizeof(remote))<0)
36 {
37 perror("connect");
38 exit(3);
39 }
40 char buf[1024];
41 ssize_t _s;
42 while(1)
43 {
44 memset(buf,‘\0‘,sizeof(buf)-1);
45 printf("please input:");
46 fflush(stdout);
47 fgets(buf,sizeof(buf)-1,stdin);
48 _s=write(client_sock,buf,sizeof(buf)-1);
49 if(_s<0)
50 {
51 perror("write");
52 exit(4);
53 }
54 _s=read(client_sock,buf,sizeof(buf)-1);
55 if(_s>0)
56 {
57 buf[_s]=‘\0‘;
58 printf("server->client:%s",buf);
59 fflush(stdout);
60 }
61 else if(_s==0)
62 {
63 close(client_sock);
64 }
65 else
66 {
67 perror("read");
68 exit(5);
69 }
70 }
71 return 0;
72 }执行结果:
本文出自 “zwy” 博客,请务必保留此出处http://10548195.blog.51cto.com/10538195/1827508
标签:select
原文地址:http://10548195.blog.51cto.com/10538195/1827508