一.基本概念
- select系统调用是用来让我们的程序监视多个文件描述符状态变化的
- 程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了改变
二.函数原型
#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptions,struct timeval *timeout);
- nfds是要监视的文件描述符最大值加1
- 第二,第三,第四个参数都是输入输出型参数(输入:用户告诉操作系统要监视的文件描述符上的特定事件;输出:操作系统告诉用户哪些文件描述符上的哪些事件已经就绪)
- timeout的结构为timeval,设置select的等待时间(设置为NULL,则为阻塞式等待;设置为0,检测状态后立即返回,并不等待外部事件的发生;设置为特定值,如果在事件内没有事件发生则超时返回)
fd_set 接口:
void FD_CLR(int fd,fd_set *set); //用来清除描述词组set中相关fd的位
int FD_ISSET(int fd,fd_set *set); //用来测试描述词组中相关fd的位是否为真
void FD_SET(int fd,fd_set *set); //用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set* set); //用来清除描述词组中set的全部位
三.select的执行过程
fd_set rfds; //定义一个可读文件描述符集合
FD_ZERO(&rfds); //清空可读文件描述符的位
FD_SET(fd,&rfds); //设置可读文件描述符相应位
select(fd+1,&rfds,NULL,NULL,NULL); //只检测可读文件描述符,select阻塞等待
if(FD_ISSET(fd,&rfds))
{
//处理事件
}
socket就绪条件
读就绪:
- socket内核中,接收缓冲区中的字节数,大于等于低水位标记SO_RCVLOWAT,此时可以无阻塞的读该文件描述符
- socket TCP通信中,对端关闭连接,此时对socket读,返回0
- 监听的socket上有新的连接请求
- socket上有未处理的错误
写就绪:
-
socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记
SO_SNDLOWAT,此时可以无阻塞的写, 并且返回值大于0; -
socket的写操作被关闭,对一个写操作被关闭的socket进行写操作,会触发SIGPIPE信号
-
socket使用非阻塞connect连接成功或者失败之后
-
socket上有未读取的错误
四.select的缺点
- 每次调用select,都需要手动设置fd集合,从接口使用角度不方便
- 每次调用select,都需要把fd从用户态拷贝到内核态,当fd多时开销过大
- 每次调用select都需要在内核遍历传递进来的所有fd,当fd很多时开销很大
- select支持的文件描述符数量太小
五.代码
1 #include<iostream>
2 #include<sys/types.h>
3 #include<sys/socket.h>
4 #include<string>
5 #include<stdlib.h>
6 #include<sys/select.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9 #include<unistd.h>
10
11
12 using namespace std;
13 class Sock
14 {
15 private:
16 int _sock;
17 string ip;
18 int port;
19 public:
20 Sock(const string& ip_,const int& port_):ip(ip_),port(port_)
21 {}
22 int Socket()
23 {
24 _sock=socket(AF_INET,SOCK_STREAM,0);
25 if(_sock<0)
26 {
27 cerr<<"socket error!"<<endl;
28 exit(2);
29 }
30 }
31 int GetSocket()
32 {
33 return _sock;
34 }
35 int Bind()
36 {
37 struct sockaddr_in local;
38 local.sin_family=AF_INET;
39 local.sin_port=htons(port);
40 local.sin_addr.s_addr=inet_addr(ip.c_str());
41 if(bind(_sock,(struct sockaddr*)&local,sizeof(local))<0)
42 {
43 cerr<<"bind error"<<endl;
44 exit(3);
45 }
46 return 0;
47 }
48 int Listen()
49 {
50 if(listen(_sock,5)<0)
51 {
52 cerr<<"listen error"<<endl;
53 exit(4);
54 }
55 return 0;
56 }
57 int Accept()
58 {
59 struct sockaddr_in peer;
60 socklen_t len=sizeof(peer);
61 int sock=accept(_sock,(struct sockaddr*)&peer,&len);
62 if(sock>0)
63 {
64 return sock;
65 }
66 return -1;
67 }
68
69 ~Sock()
70 {
71 close(_sock);
72 }
73 };
74
75 #define FD_DEFAULT -1
76 class FdArray
77 {
78 private:
79 int *fd_array;
80 int max_fd;
81 int size;
82 int cap;
83 public:
84 FdArray(int num):size(0),cap(num)
85 {
86 fd_array=new int[num];
87 for(auto i=0;i<num;++i)
88 {
89 fd_array[i]=FD_DEFAULT;
90 }
91 }
92 int AddFd(int fd)
93 {
94 if(size==cap)
95 return-1;
96 fd_array[size]=fd;
97 size++;
98 }
99 int DelFd(int fd)
100 {
101 for(auto i=0;i<size;++i)
102 {
103 if(fd_array[i]==fd)
104 {
105 fd_array[i]=fd_array[size-1];
106 fd_array[size-1]=FD_DEFAULT;
107 size--;
108 break;
109 }
110 }
111 }
112 void SetReadFds(fd_set &rfds)
113 {
114 for(auto i=0;i<size;++i)
115 {
116 if(fd_array[i]!=FD_DEFAULT)
117 {
118 FD_SET(fd_array[i],&rfds);
119 }
120 }
121 }
122 int MaxFd()
123 {
124 int max=fd_array[0];
125 for(auto i=1;i<size;++i)
126 {
127 if(max<fd_array[i])
128 max=fd_array[i];
129 }
130 return max;
131 }
132 void HandlerEvents(fd_set &rfds,Sock &listen_sock)
133 {
134 for(auto i=0;i<size;++i)
135 {
136 if(FD_ISSET(fd_array[i],&rfds))
137 {
138 if(fd_array[i]==listen_sock.GetSocket())
139 {
140 int sock=listen_sock.Accept();
141 if(sock>0)
142 {
143 cout<<"Get a new link..."<<endl;
144 AddFd(sock);
145 }
146 }
147 else
148 {
149 char buf[4096];
150 ssize_t s=recv(fd_array[i],buf,sizeof(buf)-1,0);
151 if(s>0)
152 {
153 buf[s]=0;
154 cout<<buf<<endl;
155 string http_echo="HTTP/1.0 200 OK\r\n\r\n<html><h1>hello select server!!!</h1></html>";
156 send(fd_array[i],http_echo.c_str(),http_echo.size(),0);
157 close(fd_array[i]);
158 DelFd(fd_array[i]);
159 }
160 else if(s==0)
161 {
162 cout<<"client quit"<<endl;
163 close(fd_array[i]);
164 DelFd(fd_array[i]);
165 }
166 else{
167 }
168
169 }
170 }
171 }
172 }
173
174 ~FdArray()
175 {
176 delete []fd_array;
177 }
178 };
179
180 void Usage(string proc_)
181 {
182 cout<<"Usage:"<<proc_<<"ip port"<<endl;
183 }
184 int main(int argc,char *argv[])
185 {
186 if(argc!=3){
187 Usage(argv[0]);
188 exit(1);
189 }
190
191 string ip=argv[1];
192 int port=atoi(argv[2]);
193 Sock listen_sock(ip,port);
194 listen_sock.Socket();
195 listen_sock.Bind();
196 listen_sock.Listen();
197
198 FdArray fd_array(sizeof(fd_set)*8);
199 fd_array.AddFd(listen_sock.GetSocket());
200
201 for(;;)
202 {
203 fd_set rfds;
204 FD_ZERO(&rfds);
205 fd_array.SetReadFds(rfds);
206
207 struct timeval timeout={0,0};
208 int max_fd=fd_array.MaxFd();
209
210 switch(select(max_fd+1,&rfds,NULL,NULL,/*&timeout*/NULL)){
211 case 0:
212 cout<<"timeout..."<<endl;
213 break;
214 case -1:
215 cout<<"select error"<<endl;
216 break;
217 default:
218 fd_array.HandlerEvents(rfds,listen_sock);
219 break;
220 }
221 }
222 }