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

本文为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有明显的优势


你可能感兴趣的:(网络编程学习_IO复用:select/poll)