Linux關於IO複用(poll模型)
POLL函式概念
Poll函式和select類似,但它是用檔案描述符而不是條件的型別來組織資訊的. 也就是說,一個檔案描述符的可能事件都儲存在struct pollfd中.與之相反,select用事件的型別來組織資訊,而且讀,寫和錯誤情況都有獨立的描述符掩碼.poll函式是POSIX:XSI擴充套件的一部分,它起源於UNIX System V
函式poll原型
包含標頭檔案<poll.h> 功能:與select函式功能相同 原型: int poll(struct pollfd *fdarray,unsigned long nfds,int timeout); 引數 fdarray是一個pollfd的機構體陣列用來表示表示檔案描述符的監視資訊 nfds引數給出了要監視的描述符數目 timeout引數是一個用豪秒錶示的時間,是poll在返回前沒有接收事件是應等待的時間,如果timeout的值為-1,poll就永遠不會超時.如果整數值為32個位元,那麼最大超時週期約為30分鐘 返回值:準備好描述字的個數,0-超時,1-出錯
Pollfd結構體
fd是檔案描述符值 event和revents是通過代表各種事件的標準符進行邏輯或運算構建而成的
Struct pollfd {
int fd;
short events; //感興趣的事件
short revents; //fd上觸發的事情
}
Poll函式事件標誌
事件標誌符 |
含義 |
POLLIN |
無阻塞地讀除了具有高優先順序的資料之外的資料 |
POLLRONORM |
無阻塞地讀常規資料 |
POLLRDBAND |
無阻塞地讀具有優先順序的資料 |
POLLOUT |
無阻塞的寫常規資料 |
Poll函式實現
與之前的select實現基本一致,實現c/s架構
首先伺服器:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #include <netinet/in.h> /* for struct sockaddr_in*/ #include <sys/errno.h> #include <signal.h> #include <sys/select.h> #include <poll.h> //關於IO複用伺服器的poll #define LISTEN_SIZE 1024 void error_exit(char *name) { perror(name); exit(-1); } int main(int argc,char *argv[]) { int sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0) { error_exit("create error"); } //繫結地址(ip和埠號) struct sockaddr_in svraddr; memset(&svraddr,0,sizeof(svraddr)); svraddr.sin_family=AF_INET; svraddr.sin_addr.s_addr=INADDR_ANY; //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二種寫法 svraddr.sin_port=htons(5555); int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr)); if(ret<0) { error_exit("bind error"); } //設定監聽引數back login 半連線數最大 ret=listen(sockfd,1024); if(ret<0) { error_exit("listen error"); } //建立poll結構體 struct pollfd pollfds[LISTEN_SIZE]={0}; int i=0; for(;i<LISTEN_SIZE;i++) { pollfds[i].fd=-1; } pollfds[0].fd=sockfd; pollfds[0].events=POLLIN; struct sockaddr_in removeaddr; int addr_len=sizeof(removeaddr); char *buf[1024]={0}; while(1) { int nevent=poll(pollfds,LISTEN_SIZE,-1); if(nevent==0) { printf("timeout\n"); continue; } else if(nevent<0) { error_exit("poll error"); } if(pollfds[0].revents&POLLIN) { int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len); if(fd<0) { error_exit("accept error"); } for(i=1;i<LISTEN_SIZE;i++) { if(pollfds[i].fd==-1) { pollfds[i].fd=fd; pollfds[i].events=POLLIN; break; } } for(i=1;i<LISTEN_SIZE;i++) { if(pollfds[i].fd==-1) { continue; } //判斷是否為可讀事件 if(pollfds[i].revents&POLLIN) { int rdsize=read(pollfds[i].fd,buf,1024); if(rdsize<=0) { printf("close %d\n",pollfds[i].fd); close(pollfds[i].fd); pollfds[i].fd=-1; } else { printf("read buf =%s,fd=%d\n",buf,pollfds[i].fd); } } } } } }
客戶端:
使用之前的程式碼,基本不變,貼上來
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#include <sys/errno.h>
#include <signal.h>
//關於客戶端的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("run program+ip+port\n");
return-1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//連線伺服器,設定伺服器的地址(ip和埠)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr= inet_addr(argv[1]);
svraddr.sin_port=htons(atoi(argv[2]));
int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("connect error");
}
write(sockfd,"hello",strlen("hello"));
sleep(5);
close(sockfd);
return 0;
}
結果:
客戶端執行後臺操作
伺服器接受到的結果
總結:相比較之前的select函式,poll沒有FD_SETSIZE這個限制,poll的出現可以解決select的fd數量限制