select,poll,epoll用法
select用法
???????#include <sys/time.h>
?????? #include <sys/types.h>
?????? #include <unistd.h>
?????? #include <sys/select.h>
???????int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
?????? int? pselect(int? n,? fd_set? *readfds,? fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const
?????? sigset_t *sigmask);
???????FD_CLR(int fd, fd_set *set);
?????? FD_ISSET(int fd, fd_set *set);
?????? FD_SET(int fd, fd_set *set);
?????? FD_ZERO(fd_set *set);
Select在Socket編程中還是比較重要的,可是對于初學Socket的人來說都不太愛用Select寫程序,他們只是習慣寫諸如
connect、accept、recv或recvfrom這樣的阻塞程序(所謂阻塞方式block,顧名思義,就是進程或是線程執行到這些函數時必須等
待某個事件的發生,如果事件沒有發生,進程或線程就被阻塞,函數不能立即返回)。
可是使用Select就可以完成非阻塞(所謂非阻塞方式non-
block,就是進程或線程執行此函數時不必非要等待事件的發生,一旦執行肯定返回,以返回值的不同來反映函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生則返回一個代碼來告知事件未發生,而進程或線程繼續執行,所以效率較高)方式工作的程序,它能夠監視我們需要監視的文件描述符的變化情況——讀寫或是異常。
下面詳細介紹一下!
Select的函數格式(我所說的是Unix系統下的伯克利socket編程,和windows下的有區別,一會兒說明):
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
先說明兩個結構體:
第一,struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符(filedescriptor),即文件句柄,這可以是我們所說的普通意義的文件,當然Unix下任何設備、管道、FIFO等都是文件形式,全部包括在內,所以毫無疑問一個socket就是一個文件,socket句柄就是一個文件描述符。
fd_set集合可以通過一些宏由人為來操作,比如
清空集合FD_ZERO(fd_set *);
將一個給定的文件描述符加入集合之中FD_SET(int ,fd_set
*);
將一個給定的文件描述符從集合中刪除FD_CLR(int
,fd_set*);
檢查集合中指定的文件描述符是否可以讀寫FD_ISSET(int ,fd_set* )。一會兒舉例說明。
第二,struct timeval是一個大家常用的結構,用來代表時間值,有兩個成員,一個是秒數,另一個是毫秒數。
具體解釋select的參數:
int maxfdp是一個整數值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!在Windows中這個參數的值無所謂,可以設置不正確。
fd_set*readfds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的讀變化的,即我們關心是否可以從這些文件中讀取數據了,如果這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值??梢詡魅隢ULL值,表示不關心任何文件的讀變化。
fd_set*writefds是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的寫變化的,即我們關心是否可以向這些文件中寫入數據了,如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值??梢詡魅隢ULL值,表示不關心任何文件的寫變化。
fd_set *errorfds同上面兩個參數的意圖,用來監視文件錯誤異常。
struct timeval *timeout是select的超時時間,這個參數至關重要,它可以使select處于三種狀態,第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置于阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化為止;第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大于0,這就是等待的超時時間,即select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時后不管怎樣一定返回,返回值同上述。
返回值:
負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件
在有了select后可以寫出像樣的網絡程序來!舉個簡單的例子,就是從網絡上接受數據寫入一個文件中。
例子:?
main()?
{?
??? int sock;?
??? FILE *fp;?
??? struct fd_set fds;?
??? struct timeval timeout={3,0}; //select等待3秒,3秒輪詢,要非阻塞就置0?
??? char buffer[256]={0}; //256字節的接收緩沖區?
??? /* 假定已經建立UDP連接,具體過程不寫,簡單,當然TCP也同理,主機ip和port都已經給定,要寫的文件已經打開?
??? sock=socket(...);?
??? bind(...);?
??? fp=fopen(...); */?
??? while(1)?
?? {?
??????? FD_ZERO(&fds); //每次循環都要清空集合,否則不能檢測描述符變化
??????? FD_SET(sock,&fds); //添加描述符?
??????? FD_SET(fp,&fds); //同上
??????? maxfdp=sock>fp?sock+1:fp+1;??? //描述符最大值加1
??????? switch(select(maxfdp,&fds,&fds,NULL,&timeout))?? //select使用?
??????? {?
??????????? case -1: exit(-1);break; //select錯誤,退出程序?
??????????? case 0:break; //再次輪詢
??????????? default:?
????????????????? if(FD_ISSET(sock,&fds)) //測試sock是否可讀,即是否網絡上有數據
????????????????? {?
??????????????????????? recvfrom(sock,buffer,256,.....);//接受網絡數據?
??????????????????????? if(FD_ISSET(fp,&fds)) //測試文件是否可寫?
??????????????????????????? fwrite(fp,buffer...);//寫入文件?
???????????????????????? buffer清空;?
?????????????????? }// end if break;?
????????? }// end switch?
???? }//end while?
}//end main
?
poll用法
?????? #include <sys/poll.h>
???????int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
?
?
?????????????? struct pollfd {
?????????????????????? int fd;?????????? /* file descriptor */
?????????????????????? short events;???? /* requested events */
?????????????????????? short revents;??? /* returned events */
?????????????? };
?
poll ()接受一個指向結構'struct?pollfd'列表的指針,其中包括了你想測試的文件描述符和事件。事件由一個在結構中事件域的比特掩碼確定。當前 的結構在調用后將被填寫并在事件發生后返回。在SVR4(可能更早的一些版本)中的?"poll.h"文件中包含了用于確定事件的一些宏定義。事件的等待 時間精確到毫秒?(但令人困惑的是等待時間的類型卻是int),當等待時間為0時,poll()函數立即返回,-1則使poll()一直掛起直到一個指定 事件發生。下面是pollfd的結構。?
?????struct?pollfd?{
?????????int?fd;????????/*?文件描述符?*/
?????????short?events;??/*?等待的事件?*/
?????????short?revents;?/*?實際發生了的事件?*/
?????};
??????
于select()十分相似,當返回正值時,代表滿足響應事件的文件描述符的個數,如果返回0則代表在規定事件內沒有事件發生。如發現返回為負則應該立即查看?errno,因為這代表有錯誤發生。?
如果沒有事件發生,revents會被清空,所以你不必多此一舉。?
這里是一個例子?
???/*?檢測兩個文件描述符,分別為一般數據和高優先數據。如果事件發生
??????則用相關描述符和優先度調用函數handler(),無時間限制等待,直到
??????錯誤發生或描述符掛起。*/
???
???#include?<stdlib.h>
???#include?<stdio.h>
??
???#include?<sys/types.h>
???#include?<stropts.h>
???#include?<poll.h>
??
???#include?<unistd.h>
???#include?<errno.h>
???#include?<string.h>
??
???#define?NORMAL_DATA?1
???#define?HIPRI_DATA?2
??
???int?poll_two_normal(int?fd1,int?fd2)
???{
???????struct?pollfd?poll_list[2];
???????int?retval;
??
???????poll_list[0].fd?=?fd1;
???????poll_list[1].fd?=?fd2;
???????poll_list[0].events?=?POLLIN|POLLPRI;
???????poll_list[1].events?=?POLLIN|POLLPRI;
??
???????while(1)
???????{
???????????retval?=?poll(poll_list,(unsigned?long)2,-1);
???????????/*?retval?總是大于0或為-1,因為我們在阻塞中工作?*/
??
???????????if(retval?<?0)
???????????{
???????????????fprintf(stderr,"poll錯誤:?%s/n",strerror(errno));
???????????????return?-1;
???????????}
????
???????????if(((poll_list[0].revents&POLLHUP)?==?POLLHUP)?||
??????????????((poll_list[0].revents&POLLERR)?==?POLLERR)?||
??????????????((poll_list[0].revents&POLLNVAL)?==?POLLNVAL)?||
??????????????((poll_list[1].revents&POLLHUP)?==?POLLHUP)?||
??????????????((poll_list[1].revents&POLLERR)?==?POLLERR)?||
??????????????((poll_list[1].revents&POLLNVAL)?==?POLLNVAL))
?????????????return?0;
??
???????????if((poll_list[0].revents&POLLIN)?==?POLLIN)
?????????????handle(poll_list[0].fd,NORMAL_DATA);
???????????if((poll_list[0].revents&POLLPRI)?==?POLLPRI)
?????????????handle(poll_list[0].fd,HIPRI_DATA);
???????????if((poll_list[1].revents&POLLIN)?==?POLLIN)
?????????????handle(poll_list[1].fd,NORMAL_DATA);
???????????if((poll_list[1].revents&POLLPRI)?==?POLLPRI)
?????????????handle(poll_list[1].fd,HIPRI_DATA);
???????}
???}
?
epoll用法
???????#include <sys/epoll.h>
???????int epoll_create(int size)
???????int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
?????? int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
?
??????????? typedef union epoll_data {
???????????????? void *ptr;
???????????????? int fd;
???????????????? __uint32_t u32;
???????????????? __uint64_t u64;
??????????? } epoll_data_t;
??????????? struct epoll_event {
???????????????? __uint32_t events;? /* Epoll events */
???????????????? epoll_data_t data;? /* User data variable */
??????????? };
在linux中,驚群現象已經消失了的,我們可以看???http://simohayha.javaeye.com/blog/561424?,但是當我們在開發服務器時候,需要使用epoll,發現一個問題,就是當一個請求過來的時候,發現有的時候被喚起的進程不止一個,看下面的程序:
?
當請求過來的時候,會出現accept error,而且我發現這個時候的socket id 都是 -1。我們可以在這里添加判斷來解決問題,至于為什么epoll會引入這個問題,暫時還不清楚,需要進一步的學習。
?轉載于:https://www.cnblogs.com/xuxm2007/archive/2011/08/18/2144980.html
總結
以上是生活随笔為你收集整理的select,poll,epoll用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Excel 的点点滴滴
- 下一篇: 从Hadoop框架与MapReduce模