#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
ndfs:select监视的文件句柄数,视进程中打开的文件数而定,一般设为要监视各文件中的最大文件描述符值加1。
readfds:这个文件描述符集合监视文件集中的任何文件是否有数据可读,当select函数返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符。
writefds:这个文件描述符集合监视文件集中的任何文件是否有数据可写,当select函数返回的时候,writefds将清除其中不可写的文件描述符,只留下可写的文件描述符。
exceptfds:这个文件集将监视文件集中的任何文件是否发生错误,其实,它可用于其他的用途,例如,监视带外数据OOB,带外数据使用MSG_OOB标志发送到套接字上。当select函数返回的时候,exceptfds将清除其中的其他文件描述符,只留下标记有OOB数据的文件描述符。
timeout:本次select()的超时结束时间。这个参数至关重要,它可以使select处于三种状态:
(1)若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
(2)若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
(3)timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
函数的返回值:
正值:表示监视的文件集中有文件描述符符合要求
零值:表示select监视超时
负值:表示发生了错误,错误值由errno指定。
宏操作:
FD_ZERO(fd_set *set): 用来清除描述词组set的全部位FD_SET(int fd,fd_set*set): 用来设置描述词组set中相关fd的位FD_ISSET(int fd,fd_set *set): 用来测试描述词组set中相关fd 的位是否为真FD_CLR(inr fd,fd_set* set): 用来清除描述词组set中相关fd 的位
注意事项:
(1)对于可写性的检查,最好放在需要写数据的时候进行检查。如果和可读性放在同一个地方进行检查,那么select很可能每次都会因为可写性检查成功而返回。
(2)select()调用会清空传递给它的集合参数中的内容,也就是会清空readfds、writefd、exceptfds这三个指针参数所指定的描述符集合。因此,在每次调用select()之前,必须重新初始化并把需要监视的描述符填写到相应的描述符集合中。select()调用也会清空timeout指针所指向的struct timeval结构,所以在每次调用select()之前也要重新填充timeout指针所指向的struct timeval结构。
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_FD 1024
volatile int max = -1;
int listenfd ;
char buf[1024];
fd_set read_fds;
fd_set exception_fds;
int sel_fd_arr[MAX_FD];
int get_max_fd(){
int i;
for (i = MAX_FD-1; i >= 0; i--)
if (sel_fd_arr[i] == 0)
return i;
return -1;
}
void set_sel_events(){
int i;
for (i = 0; i < MAX_FD; i++)
if (sel_fd_arr[i] == 0){
FD_SET(i, &read_fds);
FD_SET(i, &exception_fds);
}
}
void * sel_thread_fun(void * arg){
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
char addr_p[16];
int i = 0;
printf("sel_thread_fun begin\n");
while(1){
max = get_max_fd();
printf("max=%d\n", max);
int ret = select(max+1, &read_fds, NULL, &exception_fds, NULL);
if (ret < 0){
printf("selection failure \n");
continue;
}
for (i = 0; i < max+1; i++){
memset(buf, 0x00, sizeof(buf) );
if (FD_ISSET(i, &read_fds)){
if (i == listenfd){
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
printf("connection from %s \n", inet_ntop(AF_INET, &client_address.sin_addr, addr_p, sizeof(addr_p)));
if ( connfd < 0){
printf("errno is %d\n", errno);
close(listenfd);
}
sel_fd_arr[connfd] = 0;
}
else{
ret = recv(i, buf, sizeof(buf)-1, 0);
if (ret <= 0){
printf("recv ret=%d\n", ret);
sel_fd_arr[i] = -1;
break;
}
printf("get %d bytes of normal data:%s\n", ret, buf);
}
}
else if( FD_ISSET(i, &exception_fds) ){
ret = recv(i, buf, sizeof(buf)-1, MSG_OOB);
if (ret <= 0){
sel_fd_arr[i] = -1;
break;
}
printf("get %d bytes of oob data:%s\n", ret, buf);
}
}
set_sel_events();
}
return (int*)(1) ;
}
int main(int argc, char *argv[]){
if (argc <= 2){
printf("usage:%s ip_address port_number \n", argv[0]);
return 1;
}
int i;
for (i = 0; i < MAX_FD; i++)
sel_fd_arr[i] = -1;
char addr_p[16];
const char *ip = argv[1];
int port = atoi(argv[2]);
int ret = 0;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address) );
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
FD_ZERO(&read_fds);
FD_ZERO(&exception_fds);
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
sel_fd_arr[listenfd] = 0;
FD_SET(listenfd, &read_fds);
FD_SET(listenfd, &exception_fds);
pthread_t thd;
pthread_create(&thd, NULL, sel_thread_fun, NULL);
pthread_join(thd, NULL);
/*
while(1){
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
printf("connection from %s \n", inet_ntop(AF_INET, &client_address.sin_addr, addr_p, sizeof(addr_p)));
if ( connfd < 0){
printf("errno is %d\n", errno);
close(listenfd);
}
if ( connfd > max)
max = connfd;
FD_SET(connfd, &read_fds);
FD_SET(connfd, &exception_fds);
}
*/
close(listenfd);
return 0;
}