使用select
首先我们需要解答一些疑惑。为什么要使用select函数?
比如你有一个服务器程序,维护着N多个TCP连接,你如何去判断这些TCP连接上有数据传送过来了呢?最傻瓜的办法就是隔一定时间去循环读一次所有TCP连接对应的
再比如你想编写一个非
如此等等,简而言之,如果你需要监听一组文件描述符(
简单了解了select的功能以后,那么到底怎样调用select函数呢?
1、依赖
select函数是在系统库”sys/select.h”中声明的,其实在系统库“types.h”也include了一下“sys/select.h”,所以,我们在调用select函数之前务必加上头文件”sys/select.h”或者“types.h”。
2、依赖结构体“struct timeval”和“fd_set”
select有五个参数,三种类型,分别是int、struct timeval *和fd_set*,fd_set在”sys/select.h”定义,struct timeval在“time.h”中定义,所以也别忘了把头文件“time.h”加上。
3、需要配合宏变量FD_ZERO(fdsetp)、FD_SET(fd, fdsetp)、FD_ISSET(fd, fdsetp)和FD_CLR(fd, fdsetp)一起调用
其实这四个宏变量的调用方式类似于我们的普通函数,功能也很类似。看看下面的两个宏变量,你应该就理解了吧。
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
(1)宏变量FD_ZERO(fdsetp)用来清空整个fd_set结构体对象,形象地说就是fd_set结构体对象用来存储一组你关心的文件描述符(套接字),FD_ZERO负责清空该对象中已有的文件描述符(套接字)。注意:fdsetp需要传入fd_set结构体对象的指针。
(2)宏变量FD_SET(fd, fdsetp)用来将你关心的文件描述符(套接字)fd存入fd_set结构体对象fdsetp中去。注意:fdsetp需要传入fd_set结构体对象的指针。
(3)宏变量FD_ISSET(fd, fdsetp)用来判断你指定的文件描述符(套接字)fd是否满足可读、可写或者错误条件,如果满足则返回非0值,否则返回0。注意:fdsetp需要传入fd_set结构体对象的指针。
(4)宏变量FD_CLR(fd, fdsetp)用来将你指定的文件描述符(套接字)fd从指定fd_set对象fdsetp中清除,即表示不再关心此fd。注意:fdsetp需要传入fd_set结构体对象的指针。
通常情况下,你关心文件描述符(套接字)的几种状态就定义几个fe_set对象,比如我既关心可读属性又关心可写属性,还关心错误属性,那么我就需要分别定义如下三个fd_set对象。
fd_set readfds;
fd_set writefds;
fd_set errorfds;
由此看来,你关心几种状态,也得相对应的调用几次上面四个宏变量。
4、int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
第一个参数:nfds是你关心的所有文件描述符中最大者的值加一;第二、三、四个参数:分别传入可读、可写和错误集合fd_set对象的指针&readfds、&writefds和&errorfds,如果不关心某种状态,就将某种状态对应的参数传入NULL值即可;第五个参数:timeout传入select的阻塞时间,如果传入NULL则select函数会一直阻塞直到有文件描述符可读、可写或者错误。
最后,给出一个整体的调用实例如下。
#include
#include
#include
#define SLEEP_TIME 10
#define MAX_SESSION_UP 100
int main(void)
{
int i;
int fd_num;
int accept_fd_up[MAX_SESSION_UP] = {0};
fd_set readfds;
struct timeval select_timeval;
//you can add some task here, so the fd in accept_fd_up maybe changed here
while (1)
{
FD_ZERO(&readfds);
for (i=0; i
{
if (accept_fd_up[i] > max_sock)
max_sock = accept_fd_up[i];
if (accept_fd_up[i] > 0)
FD_SET(accept_fd_up[i], &readfds);
}
fd_num = select(max_sock+1, &readfds, NULL, NULL, &select_timeval);
if (fd_num < 0)
{
perror("select");
continue;
}
else if (0 == fd_num)
{
printf("select timeout...\n");
//resert the select_time after timeout
select_timeval.tv_sec = SLEEP_TIME;
select_timeval.tv_usec = 0;
//you can add some task here, so the task could work a time every SLEEP_TIME
//the fd in accept_fd_up maybe changed here
continue;
}
for (i=0; i
{
if (FD_ISSET(accept_fd_up[i], &readfds))
{
printf("do case in do_fd_up: %d,%d", accept_fd_up[i], i);
//you can add some task here to deal with the change of the fd
//the fd in accept_fd_up also maybe changed here
}
}
}
}
该实例仅仅只是一个框架而已,它假设有一组大小为MAX_SESSION_UP的int类型数组存储了一堆文件描述符,其中凡是大于0的则默认为我们关心的文件描述符,需要select监管它,小于等于0的则被忽视。
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。