Socket 程式設計IO Multiplexing

2021-09-08 17:06:17 字數 4103 閱讀 7623

linux socket 程式設計中i/o multiplexing 主要通過三個函式來實現:select, poll,epoll來實現。i/o multiplexing,先構造一張有關描述符的列表,然後呼叫乙個函式,直到這些描述符中的乙個已準備好進行i/o時,該函式才返回。在返回時,它告訴程序哪些描述符已準備好可以進行i/o。本文具體介紹一下select 和poll的用法,給出簡單的demo**,簡要分析一下這兩個函式的使用易出錯的地方。        

#includeselect.h>

intselect(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,fd_set *restrict exceptfds, struct timeval*restrict tvptr);

//returns: count of ready descriptors, 0 on timeout, -1 on error

中間三個引數readfds、writefds和exceptfds是指向描述符集的指標。這三個描述符集說明了我們關心的可讀、可寫或出於異常條件的各個描述符,設定為null則表示不關心。每個描述符集存放在乙個fd_set資料型別中。這種資料型別為每一可能的描述符保持一位。描述符集的函式介面(可能實現為巨集)包括:呼叫fd_zero將乙個指定的fd_set變數的所有位設定為0;呼叫fd_set設定乙個fd_set變數的指定位;呼叫fd_clr將一指定位清楚;呼叫fd_isset測試一指定位是否設定。

#include select.h>

int fd_isset(int fd, fd_set *fdset);

//returns: nonzero if fd is in set, 0 otherwise

void fd_clr(int fd, fd_set *fdset);

void fd_set(int fd, fd_set *fdset);

void fd_zero(fd_set *fdset);

檔案描述符集fdset中的檔案描述符的個數是有限制的,最大值由fd_setsize指定,一般為1024.

select 最後乙個引數用於設定超時值,當select監聽達到超時值時還未有關心的事件發生則返回,函式返回值為0.

struct

timeval

超時引數如果設定為 null 則無限等待。

下面來是乙個簡單的select echo server:

// ******echo.cpp

#include #include

#include

in.h>#include

#include

#include

#include

#include

#include

#include

#define sever_port 1314

#define max_line_len 1024

using

namespace

std;

intmain()

listen(listenfd ,

10);

fd_zero(&allset);

fd_set(listenfd ,&allset );

maxfd =listenfd ;

maxid = -1

;

while(1

)

if(fd_isset (listenfd , &rset ))

}printf(

"client[%d] = %d\n

",ix ,connfd );

if( fd_setsize ==ix)

if( connfd >maxfd)

fd_set(connfd, &allset );

if(ix >maxid )

if(--nready == 0

)

}for(ix = 0 ; ix <= maxid; ix++) //

<=

if(fd_isset (sockfd ,&rset ))

else

}if(--nready == 0

) }}

return0;

}

在使用select 的時候要注意兩點:

第乙個引數需要是當前所關心的檔案描述符中最大的乙個+1

第二需要注意的是select的中間3個引數採用了「value-result」(unp1的說法)的方式,設定了關心的檔案描述符進行select,select返回之後對應描述的fdset中只有有事件發生的對應fd會被設定,其它關心但是沒有事件發生的描述符將會從fdset中清除掉,如果不進行重新賦值,下次select就不會關注這些描述符了,因此上述**中allset每次對rset進行複製。

來看看如果只在while(1) 之前設定rset,在while(1) 中不在每次select之前賦值會發生什麼,在控制台輸入:strace ./******echo,另外開啟乙個控制台視窗輸入:nc localhost 1314,這作為乙個連線echo server 的 client,然後輸入你想發往echo server內容。關鍵我們來看一下echo server的情況:

可以看到 select 首先關注的檔案描述符 fd == 3,該描述符是listenfd,然後有client連過來,select關注了 fd 3 和 4,4是accept函式開啟的用於與client通訊的描述符,當client向server寫資料之後select關注的描述就只剩下 fd 4了,也就是當前處於連線狀態的描述符,如果client主動關閉,select返回之後,下次監聽就沒有關注的描述符了,可見select函式的「value-result」 返回方式是這樣工作的:每次只返回監聽描述符中處於active的,其它處於監聽的但是當前沒有事件發生的描述符則會從監聽的fdset中清除掉。因此在每次select之前需要給關注的fdset重新賦值。

注1:在進行系統呼叫除錯的時候 strace 是乙個利器,簡單使用方式如上面在執行程式之前加上 strace 即可。在除錯**邏輯的時候當然還是使用gdb了。

注2:netcat 或者叫 nc 是 linux 下的乙個用於除錯和檢查網路工具包。可用於建立 tcp/ip 連線,最大的用途就是用來處理 tcp/udp 套接字。

select 什麼時候會處於準備好並返回呢? unpv1 上進行了詳細介紹:

下面四個條件任何乙個滿足的時候套件字準備好讀:

1. 套介面接受緩衝區的資料位元組數大於等於套介面接受緩衝區的低潮限度當前值。對這樣的套介面讀操作將不阻塞並返回乙個大於0的值(既準備好讀入的資料量)。我們可以用套介面選項so_rcvlowat來設定此低潮限度,對於tcp和udp套介面,其預設值為1。

2. 連線的讀這一半關閉(也就是接收了fin的tcp連線)。對這樣的套介面讀操作將不阻塞並返回0(記檔案結束符)。

3. 套介面是乙個監聽的套介面且已完成的連線數為非0。正常情況下這樣的套介面上的accpet不會被阻塞。

4. 有乙個套介面錯誤待處理。對這樣的套介面操作將不阻塞並返回乙個錯誤-1,errno設定成明確的錯誤條件。

以下三個條件的任何乙個滿足時,套介面準備好寫操作:

1. 套介面傳送緩衝區中可用空間的位元組數大於等於套介面傳送緩衝區低潮限度的當前值,且或者(i)套介面已連線,或者(ii)套介面不需要連線(例如udp套接字)。這意味著,如果我們將這樣的套介面設定為非阻塞,寫操作將不阻塞且返回乙個正值(例如由傳輸層傳入的位元組數)。我們可以用套介面選項so_sndlowat來設定此低潮限度,對於tcp和udp套介面其預設值為2048.

2. 連線的寫這一半關閉,對這樣的套介面寫操作將產生訊號sigpipe。

3. 有乙個套介面錯誤待處理。對這樣的套介面操作寫操作將不阻塞且返回乙個錯誤-1,errno設定成明確的錯誤條件。這些待處理的錯誤也可通過指定套介面選項so_error呼叫getsockopt來取得並清除。

如果乙個套介面存在帶外資料或者仍處於帶外標記,那他有異常條件待處理。

但,i/o multiplexing 就是這樣用的嗎?

socket程式設計

一直以為serversocket accept之後客戶端才能發資訊,實驗後得出如下結論 1 serversocket沒有accept時,client是可以傳送資訊到server端的。2 serversocket accept之後,正在處理訊息時,client也是可以傳送資訊到server端。如果se...

Socket程式設計

對tcp ip udp socket程式設計這些詞你不會很陌生吧?隨著網路技術的發展,這些詞充斥著我們的耳朵。那麼我想問 1.什麼是tcp ip udp?2.socket在 呢?3.socket是什麼呢?4.你會使用它們嗎?什麼是tcp ip udp?tcp ip transmission cont...

socket程式設計

建立socket 建立乙個 socket,它可用於在基於 tcp ip 的網路 如 internet 上通訊。socket s new socket addressfamily.internetwork,sockettype.stream,protocoltype.tcp 若要使用 udp 而不是 ...