1. select 函数
复用 IO 还有一种就是 select 模型, 我们下面就来简单介绍一下 select 用法.
1.1 select 函数原型
- int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
- //fd_set 是一个集合, 里面存放的是文件描述符, 如下是一些可以操作 fd_set 的宏:
- fd_set set;
- FD_ZERO(&set); // 清空 set
- FD_SET(fd, &set); // 将 fd 加入 set
- FD_CLR(fd, &set); // 将 fd 从 set 中清除
- 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 函数:
- struct timeval TimeOut, *pTimeOut = NULL;
- fd_set readfds, writefds;
- FD_ZERO (&readfds);
- FD_SET (m_nSock, &readfds);
- writefds = readfds;
- TimeOut.tv_sec = 5;
- TimeOut.tv_usec=0;
- pTimeOut = &TimeOut;
- while(1)
- {
- if ((nRetVal = select (m_nSock + 1,&readfds, &writefds, NULL, pTimeOut)) == 0)
- {
- // 超时
- return 0;
- }
- else if ((nRetVal < 0) && (errno == EINTR || errno == EPIPE))
- continue;
- }
2. 使用 select 如何检测连接已经关闭
如果连接断开了, select 会返回 1, 但单纯的 select 返回 1 并不能说明连接断开了, 也可能是有数据可读, 所以此时需要再判断一下 read 或者 recv 的返回值, 如果返回 0, 就说明连接断开了.
伪代码如下:
- fd_set read_set;
- struct timeval t_o;
- FD_ZERO(&read_set);
- FD_SET(lSockFd,&read_set);
- t_o.tv_sec = n;/* 超时秒数 */
- ret = select(lSockFd + 1,&read_set,NULL,NULL,&t_o);
- if(ret == 1)
- {
- count = recv(lSockFd,buf,LEN,0);
- if((count == 0))
- {
- // 说明连接断开
- }
- }
3. select 和 epoll 的区别
select 是轮询 fd, 而 epoll 是先将文件描述符注册到内核, 一旦文件描述符发生变化, 内核会采用回调机制激活这个文件描述符, 这样 epoll_wait 就会知道;
epoll 监视的文件描述符数量没有限制, 当然跟内存也有关, 而 select 则在内核头文件中定义最多监视 1024 个文件描述符;
epoll 效率不会随着文件描述符数量的增长而直线下降;
epoll 使用 mmap 加速内核和用户空间的信息传递, 避免多余的内存拷贝;
本人在简书上写的内容均为本人原创, 转载需经本人同意. 简书主页: https://www.jianshu.com/u/e8c7bb5e3257
来源: http://www.jianshu.com/p/eae4b2ab2e1c