IO多路復用之select

2021-08-04 18:07:44 字數 3301 閱讀 4377

1、背景知識

我們首先來看看伺服器程式設計的模型,客戶端發來的請求服務端會產生乙個程序來對其進行服務,每當來乙個客戶請求就產生乙個程序來服務,然而程序不可能無限制的產生,因此為了解決大量客戶端訪問的問題,引入了io復用技術。

即:乙個程序可以同時對多個客戶請求進行服務。

也就是說io復用的「介質」是程序(準確的說復用的是select和poll,因為程序也是靠呼叫select和poll來實現的),復用乙個程序(select和poll)來對多個io進行服務,雖然客戶端發來的io是併發的但是io所需的讀寫資料多數情況下是沒有準備好的,因此就可以利用乙個函式(select和poll)來監聽io所需的這些資料的狀態,一旦io有資料可以進行讀寫了,程序就來對這樣的io進行服務。

io多路復用是指核心一旦發現程序指定的乙個或者多個io條件準備讀取,它就通知該程序。io多路復用適用如下場合:

(1)當客戶處理多個描述字時(一般是互動式輸入和網路套介面),必須使用i/o復用。

(2)當乙個客戶同時處理多個套介面時,而這種情況是可能的,但很少出現。

(3)如果乙個tcp伺服器既要處理監聽套介面,又要處理已連線套介面,一般也要用到i/o復用。

(4)如果乙個伺服器即要處理tcp,又要處理udp,一般要使用i/o復用。

(5)如果乙個伺服器要處理多個服務或多個協議,一般要使用i/o復用。

與多程序和多執行緒技術相比,i/o多路復用技術的最大優勢是系統開銷小,系統不必建立程序/執行緒,也不必維護這些程序/執行緒,從而大大減小了系統的開銷。

2、select函式

該函式准許程序指示核心等待多個事件中的任何乙個傳送,並只在有乙個或多個事件發生或經歷一段指定的時間後才喚醒。函式原型如下:

#include 

#include

intselect(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

返回值:就緒描述符的數目,超時返回0,出錯返回-1

函式引數介紹如下:

(1)第乙個引數maxfdp1指定待測試的描述字個數,它的值是待測試的最大描述字加1(因此把該引數命名為maxfdp1),描述字0、1、2…maxfdp1-1均將被測試。

因為檔案描述符是從0開始的。

(2)中間的三個引數readset、writeset和exceptset指定我們要讓核心測試讀、寫和異常條件的描述字。如果對某乙個的條件不感興趣,就可以把它設為空指標。struct fd_set可以理解為乙個集合,這個集合中存放的是檔案描述符,可通過以下四個巨集進行設定:

void fd_zero(fd_set *fdset);           //清空集合

void fd_set(int fd, fd_set *fdset); //將乙個給定的檔案描述符加入集合之中

void fd_clr(int fd, fd_set *fdset); //將乙個給定的檔案描述符從集合中刪除

int fd_isset(int fd, fd_set *fdset); // 檢查集合中指定的檔案描述符是否可以讀寫

(3)timeout告知核心等待所指定描述字中的任何乙個就緒可花多少時間。其timeval結構用於指定這段時間的秒數和微秒數。

struct timeval;
3.**演示(只對客戶端訊息進行讀功能):

select伺服器端:

#include

#include

#include

#include

#include

#include

#include

#include

int startup(char* ip, int port)

int opt = 1;

setsockopt(sock, sol_socket, so_reuseaddr, &opt, sizeof(opt));

struct sockaddr_in local;

local.sin_family = af_inet;

local.sin_port = htons(port);

local.sin_addr.s_addr = inet_addr(ip);

if(bind(sock,(struct sockaddr*)&local, sizeof(local)) < 0)

if(listen(sock, 10) < 0)

return sock;

}void usage(const

char* proc)

int main(int argc, char* argv)

int listen_sock = startup(argv[1],atoi(argv[2]));

int nums = sizeof(fd_set)*8;

int fds[nums];

int i = 1;

fds[0] = listen_sock;

for( ; i1;

}int max = listen_sock;

fd_set rfds;

while(1)

;for( i=0; iif(fds[i] > -1)}}

switch(select(max+1, &rfds, null, null, &timeout))

if((i==0)&&(fd_isset(fds[i], &rfds)))

//listen_sock

printf("get a client:[%s, %d]\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));

int j = 0;

for(;jif(fds[j] == -1)}}

else

if((i != 0)&&(fd_isset(fds[i], &rfds)))

else

if(s == 0)

else

}}//for}}

}return

0;}

select伺服器:

用telnet工具測試:

I O多路復用之select

阻塞i o模型 應用程式呼叫乙個i o函式,應用程式會一直等待資料準備好。如果資料沒有準備好,就會一直等待。只有當資料準備好,從核心拷貝到使用者空間io函式才成功返回。非阻塞i o模型 把乙個套介面設定成非阻塞告訴核心,當所有的i o操作無法完成時,不要將程序睡眠,而返回乙個錯誤資訊。此時i o操作...

I O多路復用之select

select是用於監視多個檔案描述符狀態的變化的。即用來監視檔案描述符讀 寫 異常狀態是否就緒。函式原型 int select int nfds,fd set readfds,fd set writefds,fd set exceptfds,struct timeval timeout select...

IO多路復用之select

原理 i o多路復用就通過一種機制,可以監視多個描述符,一旦某個描述符就緒,能夠通知程式進行相應的操作。select執行流程 select需要提供要監控的陣列,然後由使用者態拷貝到核心態 核心態線性迴圈監控陣列,每次都需要遍歷整個陣列 核心發現檔案描述符狀態符合操作結果,將其返回 所以對於我們監控的...