Linux 下網路 IO 的多路復用

2022-05-18 03:55:50 字數 3873 閱讀 6827

2019-10-20

關鍵字:select 與 poll

在 linux 系統下,io 總共可以分為以下四種:

1、阻塞 io;

2、非阻塞 io;

3、io多路復用;

允許同時對多個 io 進行控制。

4、訊號驅動 io;

一種非同步通訊模型。前面三種 io 都是同步型的,唯這一種是非同步型的。

阻塞 io

所謂阻塞 io 就是在呼叫相關函式時,程式的執行指標會暫停往下執行,直至這個 io 操作有結果返回為止。簡單來說就是我發起乙個 io 操作請求,你有資料就返回給我,沒資料我就等你到有資料為止。

阻塞型 io 是最普遍使用的 io 模式,大部分的程式都是使用這一模式的。套接字在預設情況下使用的就是阻塞 io 模式。

常見的阻塞模式函式有:read, recv, recvfrom, write, send, accept, connect。 這裡需要強調,sendto 函式是非阻塞型的。

非阻塞 io

非阻塞型 io 比較乾脆。當它發起乙個 io 請求時,若 io 有資料就返回結果,若無資料則程式指標就繼續往下執行了,不會死等的。

我們可以通過兩個函式來切換阻塞型與非阻塞型 io。

1、fcntl()

假設我們需要將阻塞型 io 設定為非阻塞型 io。

int flag;

flag = fcntl(sockfd, f_getfl, 0);

flag |= o_nonblock;

fcntl(sockfd, f_setfl, flag);

這樣一來一去,就將 io 形式給更改過來了。

2、ioctl()

int b_on = 1;

ioctl(sock_fd, fionbio,  &b_on);

io 多路復用

io 多路利用其實就是 c 程式設計中的 select/poll 模型和  epoll 模型。

select 函式:

select 函式的作用就是將原本需要單獨分別監聽阻塞資源統一交由 select 來監聽。每當被 select 所監聽的資源有資料波動時,select 會採用輪詢的方式去找出哪個阻塞資源有資料過來了。並將這些有資料波動的資源儲存起來,然後中斷 select 函式的阻塞態,執行後面的**。select() 函式的原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

這個函式是乙個阻塞函式。當它返回值 0 時表示等待超時。返回值 -1 時表示等待失敗。返回值大於 0 時表示成功監測到訊號。

引數 nfds 是指 fd 的個數。由於在 linux 中,檔案描述符總是順序遞增增長的,因此這個引數也可以填入最大檔案描述符號的值再加一。即 maxfd + 1。

引數 readfds 是讀集合。

引數 writefds 是寫集合。

引數 exceptfds 是異常集合。

引數 timeout 是超時時長。這個引數沒特殊需求的話建議填 null。null值會讓 select() 一直處於等待狀態永不超時。

fd_set 是系統定義的用於存放檔案描述符的資訊的結構體,可以通過以下幾個函式來設定:

1、void fd_zero(fd_set *fdset);

對指定集合清零。

2、void fd_set(int fd, fd_set *fdset);

將 fd 加入到 fdset 集合中去。

3、void fd_clr(int fd, fd_set *fdset);

從集合中清除指定的 fd。

4、int fd_isset(int fd, fd_set *fdset);

判斷指定的 fd 是否可以讀寫。

對於 select 函式中,writefds 與 exceptfds 通常都填 null。同時,對於 select() 之前與之後的 fd_set 集合,它所包含的內容是不一樣的。在 select() 之前,我們會往 fd_set 集合中填入所有我們想監聽的資源 fd,但在 select() 之後,對應的 fd_set 集合中的資料就已經發生了變化。通常可以理解為只有有資料波動的資源集合才會出現在 select() 以後的**中。

以下是乙個運用 select() 來復用 tcp 程式設計的偽**:

以下貼出乙個運用 seletct() 模型監聽2個udp連線的客戶端與服務端的**:

#include #include 

#include

#include

#include

#include

#include

in.h>#include

#include

static

intsockfd1;

static

intsockfd2;

void

main()

else

if(fd_isset(sockfd2, &fdset))

else}}

select監聽udp服務端

#include #include 

#include

#include

#include

#include

#include

in.h>#include

#include

#include

void

main()

sin2.sin_family =af_inet;

sin2.sin_port =htons(p2);

if(inet_pton(af_inet, "

127.0.0.1

", (void *)&sin2.sin_addr) != 1

)

char *buf;

char cmd[5

];

intfd;

struct socketaddr *sin;

while(1

)

if(strlen(cmd) == 2

)

else

if(*cmd == '2'

)

else

}else

if(strlen(cmd) == 1

)

sendto(fd, buf, strlen(buf),

0, sin, sizeof

(sin1));}}

select監聽udp客戶端

以上示例**的功能是:服務端利用 select() 監聽2個udp埠。客戶端通過鍵盤輸入決定給哪個埠傳送資料,以此演示 select() 模型。

這裡額外提一點:當 select() 模型監聽到有事件發生時,最好將這一事件消費掉,即上例中在監聽到有 udp 訊息過來時,一定要通過 read() 將訊息讀出來。否則 select() 函式將在下一次監聽時立即返回。

poll 函式:

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

函式執行成功時返回大於0的值,失敗時返回 eof,超時返回值0。

引數 pollfd 是乙個系統結體體,它的原型如下:

struct pollfd ;

引數 nfds 與 timeout 就不再贅述了。

以下是乙個示例程式,程式來自於 

epoll 函式:

略。訊號驅動 io

略。

I O多路復用

一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...

i o多路復用

最常見的i o多路復用就是 select poll epoll了,下面說說他們的一些特點和區別吧。select 可讀 可寫 異常三種檔案描述符集的申明和初始化。fd set readfds,writefds,exceptionfds fd zero readfds fd zero writefds ...

I O多路復用

我們都知道unix like 世界裡,一切皆檔案,而檔案是什麼呢?檔案就是一串二進位製流而已,不管socket,還是fifo 管道 終端,對我們來說,一切都是檔案,一切都是流。在資訊 交換的過程中,我們都是對這些流進行資料的收發操作,簡稱為i o操作 input and output 往流中讀出資料...