1. 程式人生 > >I/O多路轉接之poll 函式

I/O多路轉接之poll 函式

poll

一、poll()函式:

這個函式是某些Unix系統提供的用於執行與select()函式同等功能的函式,自認為poll和select大同小異,下面是這個函式的宣告:

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

引數:

1.第一個引數:一個結構陣列,struct pollfd:

        fds:是一個struct pollfd結構型別的陣列,每個陣列元素都是一個pollfd結構,用於指定測試某個給定描述字fd的條件。存放需要檢測其狀態的Socket描述符;每當呼叫這個函式之後,系統不會清空這個陣列,操作起來比較方便;

特別是對於socket連線比較多的情況下,在一定程度上可以提高處理的效率;這一點與select()函式不同,呼叫select()函式之後,select()函式會清空它所檢測的socket描述符集合,導致每次呼叫select()之前都必須把socket描述符重新加入到待檢測的集合中;因此,select()函式適合於只檢測一個socket描述符的情況,而poll()函式適合於大量socket描述符的情況

結構如下:

 struct pollfd{
  int fd;          //檔案描述符
  short events;    //請求的事件
  short revents;   //返回的事件
  };

  events和revents是通過對代表各種事件的標誌進行邏輯或運算構建而成的。events包括要監視的事件(就是我需要關注的時間,是讀?是寫?還是出錯?)poll用已經發生的事件填充revents。poll函式通過在revents中設定標誌肌膚POLLHUP、POLLERR和POLLNVAL來反映相關條件的存在。不需要在events中對於這些標誌符相關的位元位進行設定。如果fd小於0, 則events欄位被忽略,而revents被置為0.標準中沒有說明如何處理檔案結束。檔案結束可以通過revents的識別符號POLLHUN或返回0位元組的常規讀操作來傳達。即使POLLIN或POLLRDNORM指出還有資料要讀,POLLHUP也可能會被設定。因此,應該在錯誤檢驗之前處理正常的讀操作。

poll函式的事件標誌符值:

常量 說明
POLLIN 普通或優先順序帶資料可讀
POLLRDNORM 普通資料可讀
POLLRDBAND 優先順序帶資料可讀
POLLPRI 高優先順序資料可讀
POLLOUT 普通資料可寫
POLLWRNORM 普通資料可寫
POLLWRBAND 優先順序帶資料可寫
POLLERR 發生錯誤
POLLHUP 發生掛起
POLLNVAL 描述字不是一個開啟的檔案

 注意:

        1)後三個只能作為描述字的返回結果儲存在revents中,而不能作為測試條件用於events中。

  2)第二個引數nfds:要監視的描述符的數目。

  3)最後一個引數timeout:是一個用毫秒表示的時間,是指定poll在返回前沒有接收事件時應該等待的時間。如果  它的值為-1,poll就永遠都不會超時。如果整數值為32個位元,那麼最大的超時週期大約是30分鐘。

timeout值 說明
INFTIM 永遠等待
0 立即返回,不阻塞程序
>0
等待指定數目的毫秒數

如果是對一個描述符上的多個事件感興趣的話,可以把這些常量標記之間進行按位或運算就可以了;

比如:

對socket描述符fd上的讀、寫、異常事件感興趣,就可以這樣做:

struct pollfd  fds;

fds[index].events=POLLIN | POLLOUT | POLLERR;

當 poll()函式返回時,要判斷所檢測的socket描述符上發生的事件,可以這樣做:
struct pollfd  fds;

//檢測可讀TCP連線請求:
if((fds[nIndex].revents & POLLIN) == POLLIN)
{
<span style="white-space:pre">	</span>//接收資料,呼叫accept()接收連線請求
}

//檢測可寫:
if((fds[nIndex].revents & POLLOUT) == POLLOUT)
{
<span style="white-space:pre">	</span>//傳送資料
}

//檢測異常:
if((fds[nIndex].revents & POLLERR) == POLLERR)
{
<span style="white-space:pre">	</span>//異常處理
}
二、例項TCP伺服器的伺服器程式
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <string.h>
#include <errno.h>
#include <poll.h>   //for poll

#define LISTENQ 1024
#define MAXLINE 1024
#define OPEN_MAX 50000

#ifndef INFTIM 
#define INFTIM -1 
#endif             

int start_up(char* ip,int port)  //建立一個套接字,繫結,檢測伺服器
{
  //sock
  //1.建立套接字
  int sock=socket(AF_INET,SOCK_STREAM,0);   
  if(sock<0)
  {
      perror("sock");
      exit(0);
  }
  
  //2.填充本地 sockaddr_in 結構體(設定本地的IP地址和埠)
  struct sockaddr_in local;       
  local.sin_port=htons(port);
  local.sin_family=AF_INET;
  local.sin_addr.s_addr=inet_addr(ip);

  //3.bind()繫結
  if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 
  {
      perror("bind");
      exit(1);
  }
  //4.listen()監聽 檢測伺服器
  if(listen(sock,back_log)<0)
  {
      perror("sock");
      exit(1);
  }
  return sock;    //這樣的套接字返回
}
	
int main(int argc, char *argv[])
{
  int i, maxi, connfd, sockfd;
  int nready;
  ssize_t n;
  socklen_t clilen;

  struct sockaddr_in servaddr;
    socklen_t len=sizeof(servaddr); 

   char buf[BUFSIZ];
    struct pollfd client[OPEN_MAX]; // 用於poll函式第一個引數的陣列,存放每次的檔案描述符個數
  if( argc != 3 )
    {
       printf("Please input %s <hostname>\n", argv[0]);
	 exit(2);
    }

        int listenfd=start_up(argv[1],argv[2]);      //建立一個綁定了本地 ip 和埠號的套接字描述符
		
      client[0].fd = listenfd;         //將陣列中的第一個元素設定成監聽描述字
      client[0].events = POLLIN;       //將測試條件設定成普通或優先順序帶資料可讀(感興趣的事件讀、寫、出錯),此處書中為POLLRDNORM,*/
		client[0].revents = 0;           //真正發生的事件

      for(i = 1;i < OPEN_MAX; ++i)     //陣列中的其它元素將暫時設定成不可用
		{
			client[i].fd = -1;
		}
        
      maxi = 0;
       while(1)
      {
         nready = poll(client, maxi+1,INFTIM);          //將程序阻塞在poll上
         if( client[0].revents & POLLIN)                //先測試監聽描述字
         {
				connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen);

              for(i = 1; i < OPEN_MAX; ++i)
				{
					if( client[i].fd < 0 )
                  {
                      client[i].fd = connfd;      //將新連線加入到測試陣列中
                     client[i].events = POLLIN;  //POLLRDNORM; 測試條件普通資料可讀
                      break;
                  }
              	if( i == OPEN_MAX )
               	{
                 	 	printf("too many clients"); //連線的客戶端太多了,都達到最大值了
                   	exit(1);
              	}

              	if( i > maxi )
                  	maxi = i;        //maxi記錄的是陣列元素的個數

              	if( --nready <= 0 )
                 	continue;            //如果沒有可讀的描述符了,就重新監聽連線
				}
          }

          for(i = 1; i <= maxi; i++)  //測試除監聽描述字以後的其它連線描述字
          {
				if( (sockfd = client[i].fd) < 0) //如果當前描述字不可用,就測試下一個
                  continue;

              if(client[i].revents & (POLLIN | POLLERR))//如果當前描述字返回的是普通資料可讀或出錯條件
              {
                 if( (n = read(sockfd, buf, MAXLINE)) < 0) //從套介面中讀資料
                 {
                      if( errno == ECONNRESET) //如果連線斷開,就關閉連線,並設當前描述符不可用
                      {
                         close(sockfd);
                         client[i].fd = -1;
                      }
                      else
                          perror("read error");
                  }
                else if(n == 0) //如果資料讀取完畢,關閉連線,設定當前描述符不可用
                  {
                      close(sockfd);
                      client[i].fd = -1;
                  }
                  else
                      write(sockfd, buf, n); //列印資料
               if(--nready <= 0)
                   break;
              }
          }
     }
     exit(0);
 }
賜教!

相關推薦

I/O轉接poll 函式

poll 一、poll()函式: 這個函式是某些Unix系統提供的用於執行與select()函式同等功能的函式,自認為poll和select大同小異,下面是這個函式的宣告: #include <poll.h> int poll(struct pollfd *

I/O轉接select、poll、epoll

I/O多路轉接之select 系統提供select函式來實現多路複用輸入/輸出模型。select系統呼叫是用來讓我們的程式監視多個檔案控制代碼的狀態變化的。程式會停在select這裡等待,直到被監視的檔案控制代碼有一個或多個發生了狀態改變。關於檔案

I/O轉接select伺服器

(1)每次調⽤用select,都需要把fd集合從⽤使用者態拷貝到核心態,這個開銷在fd很多時會很⼤大 (2)同時每次調⽤用select都需要在核心遍歷傳遞進來的所有fd,這個開銷在fd很多時也很⼤大 (3)select⽀支援的⽂檔案描述符數量太⼩小了,預設是10

I/O轉接select

在完成I/O操作時,程式中完成真正I/O的時間可能只有少的一部分,而大部分時間都處於一個等的時間。比如,此時需要從一個套接字中讀取資料read(socket, buf, BUFSIZE); 這個操作可能會一直阻塞,直到有資料從網路的另一端傳送過來。等的時間過於長,這是I/

I/O轉接——select、poll 和 epoll

一、select 1. select() 函式 select系統呼叫是用來讓我們的程式監視多個檔案描述符的狀態變化的; 程式會停在select這裡等待,直到被監視的檔案描述符有一個或多個發生了狀態改變。 select函

I/O轉接-epoll

原本 oid com pue tdi 數據 錯誤 系統 val By francis_hao Aug 5,2017 APUE講多路轉接的章節介紹了select、pselect和poll函數。而epoll是linux內核在2.5.44引入的。在glibc 2.3.2

select函數與I/O轉接

error strong pan 實現 tail 問題 exce 準備就緒 這樣的 select函數與I/O多路轉接 相作大家都寫過讀寫IO操作的代碼,例如從socket中讀取數據可以使用如下的代碼: while( (n = read(socketfd, buf, BU

I/O複用——poll

上一篇我們說了關於select的相關資訊,我們可以看到select是有弊端的,所以為了解決select的弊端,UNIX又在後期提出了poll。 select的弊端這裡就不多說了,上一篇部落格有提及。 poll poll和select類似,不過在一些

迴圈伺服器,併發伺服器模型以及I/O轉接模型

一、基於TCP/IP協議的基本迴圈伺服器 tcp_server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h&

網路程式設計-轉接poll與epoll模型

首先,還是需要理解io過程:io過程總體來看分兩步,第一步就是等,第二步才是資料搬遷。而如果要想提高io的效能與效率,就要減少等的比重。 可以假想一個場景: 你去釣魚,但是你只有一個魚竿。你的同伴也和你一起去釣魚,但是他帶了100個魚竿。假設每條魚上

I/O復用select,poll,epoll簡介

重新 才會 增長 文件描述 brush 重新編譯 () 情況 包含 一、select 1.起源 select最早於1983年出現在4.2BSD中(BSD是早期的UNIX版本的分支)。 它通過一個select()系統調用來監視多個文件描述符的數組,當select()返回後,該

I/O複用select、poll、epoll

很早之前有寫過篇IO多路複用的文章:https://www.cnblogs.com/klcf0220/archive/2013/05/14/3077003.html 參考連結:https://segmentfault.com/a/1190000003063859 select,poll,epoll都是IO多路

I/O復用select、poll、epoll

不同 file roc 測試 max 復用 隊列 最大 而且 很早之前有寫過篇IO多路復用的文章:https://www.cnblogs.com/klcf0220/archive/2013/05/14/3077003.html參考鏈接:https://segmentfaul

unix網絡編程——I/O復用epoll

得到 是否 再次 專用 空間 line 正常 時間服務 struct 1. 基本概念   當程序進行IO時,如果數據尚未準備好,那麽IO將處於阻塞狀態。當某個進程有多個打開的文件,比如socket,那麽其後的所有準備好讀寫的文件將受到阻塞的影響而不能操作。不借助線程,單一進

Linux網路程式設計---I/O複用select

1.I/O多路複用(IO multiplexing) 我們之前講了I/O多路複用和其他I/O的區別,在這裡,我們再具體討論下I/O多路複用是怎麼工作? I/O 多路複用技術就是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序不阻塞於某個特定的 I/O 系統呼叫。

嵌入式Linux網路程式設計,I/O複用,poll()示例,poll()客戶端,poll()伺服器,單鏈表

文章目錄 1,IO複用poll()示例 1.1,poll()---net.h 1.2,poll()---client.c 1.3,poll()---sever.c 1.4,poll()---linklist.h 1.5,p

嵌入式Linux網路程式設計,I/O複用,阻塞I/O模式,非阻塞I/O模式fcntl()/ioctl(),複用I/O select()/pselect()/poll(),訊號驅動I/O

文章目錄 1,I/O模型 2,阻塞I/O 模式 2.1,讀阻塞(以read函式為例) 2.2,寫阻塞 3,非阻塞模式I/O 3.1,非阻塞模式的實現(fcntl()函式、ioctl() 函式)

IO基礎入門I/O複用技術

在I/O程式設計過程中,當需要同時處理多個客戶端接入請求時,可以利用多執行緒或者I/O多路複用技術進行處理。I/O多路複用技術通過把多個I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單執行緒的情況下可以同時處理多個客戶端請求。與傳統的多執行緒/多程序模型比,I

淺談網路I/O複用模型 select & poll & epoll

我們首先需要知道select,poll,epoll都是IO多路複用的機制。I/O多路複用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒後自己負責進行讀寫,

I/O複用 epoll 系統呼叫

I/O多路複用除了之前我們提到的select和poll外,epoll 也可以檢查多個檔案描述符的就緒狀態,以達到I/O多路複用的目的。 epoll 系統呼叫是 Linux 系統專有的,在 Linux 核心 2.6 版本新增,epoll 的主要優點有: 當檢