#include <sys/select.h> /* According to earlier standards */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); nfds: 监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态 readfds:监控有读数据到达文件描述符集合,传入传出参数 writefds:监控写数据到达文件描述符集合,传入传出参数 exceptfds:监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数 timeout:定时阻塞监控时间,3种情况:
1.NULL,永远等下去
2.设置timeval,等待固定时间
3.设置timeval里时间均为0,检查描述字后立即返回,轮询.
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; void FD_CLR(int fd, fd_set *set);把文件描述符集合里fd清0 int FD_ISSET(int fd, fd_set *set);测试文件描述符集合里fd是否置1 void FD_SET(int fd, fd_set *set);把文件描述符集合里fd位置1 void FD_ZERO(fd_set *set);把文件描述符集合里所有位清0
select的几大缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
(3)select支持的文件描述符数量太小了,默认是1024
//============select-server代码==============
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <ctype.h> #define SELECT_MAX 1024 #define PORT 8000 #define BUF 1500 #define LISTEN 128 int main(void) { struct sockaddr_in serveraddr,clientaddr; int serverfd,clientfd,maxfd,client_len,ready,flags,i; int client[SELECT_MAX];//存放socket的数组 fd_set set,allset; char buf[BUF]; bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port=htons(PORT); serveraddr.sin_addr.s_addr=htonl(INADDR_ANY); serverfd = socket(AF_INET,SOCK_STREAM,0); bind(serverfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)); listen(serverfd,LISTEN); FD_ZERO(&allset); FD_SET(serverfd,&allset); maxfd = serverfd; flags = -1; for(i = 0;i<SELECT_MAX;i++){ client[i]=-1;//存放socket的数组 } while(1){ set = allset; ready = select(maxfd+1,&set,NULL,NULL,NULL); while(ready>0) { if(FD_ISSET(serverfd,&set)){//有要联接的对象,将新的对象放入到Client[]数组中; //处理的是监听listensocket的消息 client_len = sizeof(clientaddr); clientfd = accept(serverfd,(struct sockaddr*)&clientaddr,&client_len); for(i=0;i<SELECT_MAX;i++){ if(client[i]==-1){ client[i]=clientfd; FD_SET(clientfd,&allset); break; } } if(maxfd < clientfd) maxfd = clientfd; if(i == SELECT_MAX){ printf("select监听序列已满\n"); exit(0); } if(flags < i) flags = i; --ready; }else{//有数据要读取,遍历已连接Socket的数组,查看是否在fd_set中。 //处理的是已连接的socket的信息。 int tmpfd; int len,j; for(i = 0;i<=flags;i++){ if(FD_ISSET(client[i],&set)) { tmpfd = client[i]; break; } } len = read(tmpfd ,buf,sizeof(buf)); if(len == 0){ close(tmpfd); client[i]=-1; FD_CLR(tmpfd,&allset); }else if(len < 0){ perror("read"); exit(0); }else{ j = 0; while(j<len){ buf[j] = toupper(buf[j]); j++; } write(tmpfd,buf,len); } --ready; } } } close(serverfd); return 0; }