网络编程学习_IO复用:select/poll

来源:互联网 发布:怎么看见手机淘宝评价 编辑:程序博客网 时间:2024/05/21 10:00

本文为UNP第6章学习笔记

一, select函数

#include <sys/select.h>

int select(int maxfd,fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);

参数:

  1. maxfd: select管理的最大fd+1, 也就是说你必须自己算出最大的fd是多大
  2. readset: 需要监控可读的fd集合, 只要read/accept操作不会阻塞,就算可读状态, 包括对方已关闭(返回0),套接字出错(返回负值),可读(返回正值),有新连接(可accept)
  3. writeset 需要监控可写的fd集合,wrte操作不阻塞或非阻塞socket中connect已完成,就算可写, 包括对方已关闭(SIGPIPE信号),套接字出错(返回负值),可写, connect完成或出错
  4. exceptset 异常条件的fd集合,通常用不到, 注意socket出错不算异常,而算是可读或可写
  5. timeout: NULL:阻塞   为0的timeval: 立刻返回   非0的timeval: 带定时器的阻塞
返回值:
  1. >0: 就绪fd的数目
  2. =0:超时
  3. <0:出错
二, fd_set相关宏
fd_set用来表示一组fd集合, 一般是一个long int数组,可能的实现如下:
struct fd_set{
    long int fd_bits[16];
};
fd_set用每个位是1/0来记录fd, 16个long int共有 16*64=1024位, 因此select只能处理最多1024个fd.
fd_set可能有不同的实现,但我们只需要关注这4个操作fd_set的宏:
void FD_ZERO(fd_set* fdset);//将整个fdset置0
void FD_SET(int fd,fd_set* fdset);//将某个fd加到fdset中
void FD_CLR(int fd,fd_set* fdset);//将某个fd从fdset中删除
int FD_ISSET(int fd,fd_set* fdset);//判断某个fd是否在fdset中

三,select的性质
1,select最多只支持1024个fd
2,每次select完成后会清空各fd_set种未就绪的fd,因此:
   1,你每次select都要重新用FD_SET加入fd
   2,你需要对每个监控的fd调用一次FD_ISSET来判断这个fd是否就绪
这些性质导致select的性能随监控fd数量的增加线性下降, 更好也更流行的方法是epoll, 同时epoll的使用也更为简单

四,用select实现的单线程服务器示例:
代码不够简洁,总之:1,有一个数据结构记录所监控的fd   2,计算maxfd
              //...... init  listenfd        set<int> connected_fds;              fd_set rset;        int nready;        int maxfd;        vector<int> del_fds;        while(1)        {                FD_ZERO(&rset);                FD_SET(listenfd,&rset);                maxfd=listenfd;                for(set<int>::iterator iter=connected_fds.begin();iter!=connected_fds.end();iter++)                {                        FD_SET(*iter,&rset);                        maxfd = maxfd>*iter ? maxfd : *iter;                }                nready = select(maxfd+1,&rset,0,0,0);                if(FD_ISSET(listenfd,&rset))                {                        printf("a new connection\n");                        newfd = accept(listenfd,(sockaddr*)&client_addr,&client_addr_len);                        if(newfd<=0)                        {                                printf("accept failed:%s\n",strerror(errno));                                return -1;                        }                        connected_fds.insert(newfd);                }                for(set<int>::iterator iter=connected_fds.begin();iter!=connected_fds.end();iter++)                {                        if(FD_ISSET(*iter,&rset))                        {                                printf("connection %d was selected\n",*iter);                                ret = read(*iter,recv_buf,sizeof(recv_buf));                                if(ret==0)                                {                                        printf("connection %d closed by remote\n",*iter);                                        close(*iter);                                        del_fds.push_back(*iter);                                }else if(ret <0){                                        printf("connection %d err: %s\n",*iter,strerror(errno));                                        return -1;                                }else{                                        recv_buf[ret]='\0';                                        printf("recv:[%s]\n",recv_buf);                                        strcpy(send_buf,recv_buf);                                        write(*iter,send_buf,strlen(send_buf));                                }                        }                }                for(vector<int>::iterator iter=del_fds.begin();iter!=del_fds.end();iter++)                {                        connected_fds.erase(*iter);                }                del_fds.clear();        }

五,poll函数
#include <poll.h>
int poll(struct pollfd* fdarray, unsigned long nfds, int timeout);
参数:
  1. fdarray: 记录fd信息的数组指针
  2. nfds: 数组长度
  3. timeout: 作用与select最后一个参数相同,注意这是一个int,单位是毫秒. INFTIM:阻塞  0 非阻塞   >0:超时时间
poll提供的功能与select基本相同, 可以看错是使用了另一种方式组织管理fd信息,
struct pollfd{
     int fd; //fd
     short  events; //监控的fd状态 一般为 POLLIN或POLLOUT
     short  revents; //返回就绪的状态
};
如果fd<0, 那么poll将自动忽略这个pollfd

六, poll的性质
  1. 因为poll函数管理fd的数据是由用户自由分配的数组,因此poll没有管理fd数量的限制
  2. poll不会修改未就绪的pollfd信息, 但你仍需需要检查每个pollfd的revents来判断该fd是否就绪
 比较而言, select的使用比poll更为广泛. 不过相比之下,新一代的epoll有明显的优势


0 0
原创粉丝点击