- select函数
复用IO还有一种就是select模型,我们下面就来简单介绍一下select用法。
1.1 select函数原型
1. `int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);`
2. `//fd_set是一个集合,里面存放的是文件描述符,如下是一些可以操作fd_set的宏:`
3. `fd_set set;`
4. `FD_ZERO(&set); //清空set`
5. `FD_SET(fd, &set); //将fd加入set`
6. `FD_CLR(fd, &set); //将fd从set中清除`
7. `FD_ISSET(fd, &set); //如果fd在set中则为真,通常用来检查某个文件描述符是否在描述符集合中,然后去读、写、接收连接`
struct timeval则代表超时时间,有两个成员,一个是秒数,一个是毫秒;select参数说明:
- maxfdp 代表集合中文件描述符的范围, 即所有文件描述符的最大值加1;
- readfds 指针指向文件描述符集合,如果描述符中有一个文件可读, select就返回一个大于0的值(准备就绪的描述符数量), 表示有文件可读。没有文件可读时则判断是否超时, 若超时,返回0, 否则发生错误返回负值, 当传入NULL时, 则表示不关心是否有文件可读;
- writefds 指针指向文件描述符集合,如果描述符中有一个文件可写, select就返回一个大于0的值, 表示有文件可写。没有文件可写时则判断是否超时, 若超时,返回0, 否则发生错误返回负值, 当传入NULL时, 则表示不关心是否有文件可写;
- errorfds 同上面两个参数,用来监视文件错误异常;
- timeout 传入NULL时,没有超时时间,会将select置于阻塞状态,一直等到监视的文件描述符集合中有文件描述符发生变化为知;若将超时时间设为0,则select会处于非阻塞状态,不管文件描述符是否有变化,立即返回,文件描述符有变化时,返回大于0, 无变化时, 返回0;超时时间大于0,则如果有文件描述符发生变化才返回,否则直到超时,才返回;
1.2 select函数调用
socket()/bind()/listen()/select()/send()/recv()/close()
1.3 select使用
使用select也需先将socket设置为非阻塞的 下面演示如何使用select函数:
1. `struct timeval TimeOut, *pTimeOut = NULL;`
2. `fd_set readfds, writefds;`
3. `FD_ZERO (&readfds);`
4. `FD_SET (m_nSock, &readfds);`
5. `writefds = readfds;`
6. `TimeOut.tv_sec = 5;`
7. `TimeOut.tv_usec=0;`
8. `pTimeOut = &TimeOut`
9. `while(1)`
10. `{`
11. `if ((nRetVal = select (m_nSock + 1,&readfds, &writefds, NULL, pTimeOut)) == 0)`
12. `{`
13. `//超时`
14. `return 0;`
15. `}`
16. `else if ((nRetVal < 0) && (errno == EINTR || errno == EPIPE))`
17. `continue;`
18. `}`
- 使用select如何检测连接已经关闭
如果连接断开了,select会返回1,但单纯的select返回1并不能说明连接断开了,也可能是有数据可读,所以此时需要再判断一下read或者recv的返回值,如果返回0,就说明连接断开了。伪代码如下:
1. `fd_set read_set;`
2. `struct timeval t_o;`
3. `FD_ZERO(&read_set);`
4. `FD_SET(lSockFd,&read_set);`
5. `t_o.tv_sec = n;/* 超时秒数*/`
6. `ret = select(lSockFd + 1,&read_set,NULL,NULL,&t_o);`
7. `if(ret == 1)`
8. `{`
9. `count = recv(lSockFd,buf,LEN,0);`
10. `if((count == 0))`
11. `{`
12. `//说明连接断开`
13. `}`
14. `}`
- select和epoll的区别
-
select是轮询fd,而epoll是先将文件描述符注册到内核,一旦文件描述符发生变化,内核会采用回调机制激活这个文件描述符,这样epoll_wait就会知道;
-
epoll监视的文件描述符数量没有限制,当然跟内存也有关,而select则在内核头文件中定义最多监视1024个文件描述符;
-
epoll效率不会随着文件描述符数量的增长而直线下降;
-
epoll使用mmap加速内核和用户空间的信息传递,避免多余的内存拷贝;
更多c++及python系列文章,请关注我的公众号:晟夏的叶。