select使用
fd_set是一个long类型的数组,主要是作用与sokcet的句柄进行绑定
// 这里的fd 实际使用都是以 句柄 传入
FD_ZERO(fd_set *fdset); // 将set清零使集合中不含任何fd
FD_SET(int fd, fd_set *fdset); // 将fd加入set集合
FD_CLR(int fd, fd_set *fdset); // 将fd从set集合中清除
FD_ISSET(int fd, fd_set *fdset); // 检测fd是否在set集合中,不在则返回0
int select(int maxfds,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
maxfds:所有传入描述符的最大值加1
readfds:可读事件描述符的集合,返回的是可读事件的fd的集合,当一个fd收到数据时,代表其为可读的fd
writefds:可写事件描述符的集合,当一个fd可以发送数据时,其sendbuf不会满的,就会返回其可写事件。
timeout:为等待时间,select是个阻塞的IO,当传入-1时说明一直要等待,当传入大于0的时间,比如5s;select等待5s之后就会返回,不管其中是否可获取到就绪好的fd
readfds----> | fd1 | fd2 | fd3 | ... |
writefds-----> | fd1 | fd2 | fd3 | ... |
errorfds------> | fd1 | fd2 | fd3 | ... |
FD_ISSET():函数判断相应的集合是否准备就绪,比如readfs集合中,如果fd=5,但是没有收到数据,FD_ISSET(5,readfds)就为0。
poll:
poll与select类型,但是可以克服select的一个缺点,select是一个数组与fd进行相关联,但一个fd_set的长度是有限的;poll则使用链表去与fd相关联,理论上可以无限的。
pollfd:里面保存是fd与相应的事件(POLLIN,POLLOUT,POLLERR)代表着相应的读,写,错误事件
POLL(pollfd pollfds,int maxfd+1,struct timeval *timeout);
EPOLL:
主要的三个函数
1:int epoll_create(int size); 2:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 3:int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); |
epoll_create:创建一个epoll的句柄
epoll_ctl:注册fd等待的事件,epfd就是epoll_create返回的句柄,op为操作:epoll_add,epoll_delte,添加,删除相应的fd事件,fd为注册的fd,*event是保存其相应的注册事件epollin,epollout,读与写等事件
epoll_wait:则是阻塞监听 epoll 实例上所有的 fd 的 I/O 事件,它接收一个用户空间上的一块内存地址 (events 数组),kernel 会在有 I/O 事件发生的时候把文件描述符列表复制到这块内存地址上,然后 epoll_wait 解除阻塞并返回,最后用户空间上的程序就可以对相应的 fd 进行读写了。
epoll两种触发模式
LT(水平触发)是默认的模式,ET(边缘触发)是“高速”模式。
LT:代表着只要有数据就会一直触发,比如recvbuf中有数据存在就会一直触发,直到resv中没有数据存在
ET:代表着只会触发一直, 比如recv接受数据时,只是数据到来的时候,触发一次,下次发送数据时再触发一次。不管recvbuf中是否还存在数据,如果服务器每次接受的数据都客户端小于发送的,那么下次再发送时,进行触发;服务器就会接着上次recvbuf的位置进行读取。