I/O多路转接(select)

一.基本概念

  • 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 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值