select模型
其使用select函數實現對I/O的管理。select函數可以判斷套接字上是否有數據,或者是否能夠向套接字上寫數據。設計這可函數的目的是,爲了防止套接字處於阻塞模式時,I/O調用過程處於阻塞模式;或者當套接字處於非阻塞模式時,產生WSAEWOULDBLOCK錯誤。如果不滿足實現規定的參數條件,那麼select函數在進行I/O操作時會阻塞。其定義如下:
- int select(
- int nfds,
- fd_set FAR* readfds,
- fd_set FAR* writefds,
- fd_set FAR* exceptfds,
- const struct timeval FAR* timeout
- );
int select(
int nfds,
fd_set FAR* readfds,
fd_set FAR* writefds,
fd_set FAR* exceptfds,
const struct timeval FAR* timeout
);
- nfds:這個參數會被忽略。
- readfds:其爲sd_set類型,其是一系列套接字的集合,用於檢查可讀性。這個集合要滿足下面條件之一:1)有數據可讀入 2)連接已經被關閉、重啓或終止 3)假如已經調用listen,且有一個連接處於擱置狀態,那麼accept調用成功。
- wrtiefds:用於檢查可寫性。其套接字要滿足下面條件之一:1)有數據發出 2)如果正在對一個非阻塞連接調用進行處理,則連接就成功了。
- exceptfds:用於帶外數據。其套機字要滿足下面條件之一:1)加入正在對一個非阻塞連接調用進行處理,連接嘗試機會失敗。 2)有00B數據可讀操作。
- timeout:其一個指向timeval結構體的指針,用於表示select函數在調用返回前的等待時間,如果爲空指針({0,0}),表示無限期等待。不爲0,表示其中至少一個套接字滿足條件。
- 返回值:如果select調用成功,會在fd_set結構中,返回被掛起的I/O操作的所有套接字句柄總量。超時,返回0。失敗,返回SOCKET_ERROR。
select返回後,會對每個fd_set結構體進行修改,會將那些不存在被掛起I/O操作的套接字刪除。也就是說,我們可以通過FD-ISSET宏來判斷等待的套接字是還處於宏中。
timeval結構體定義如下:
- struct timeval
- {
- long tv_sec;
- long tv_usec;
- };
struct timeval
{
long tv_sec;
long tv_usec;
};
- tv_sec:以秒爲單位指定等待時間。
- tv_usec:以毫秒爲單位指定等待時間。
在用select函數對套接字進行監聽前,需要將套接字分配給一個集合。對fd_set集合進行處理與檢查的宏:
- FD_ZERO(*,set):將set集合初始化爲空。
- FD_CLR(s,* set):從set中刪除套接字s.
- FD_ISSET(s,* set):從集合set中檢查s是否在其中;是,就返回TRUE。
- FD_SET(s,* set):將套接字s加入集合set中。
- FD_SETSIZE:對fd_set結構中的最多套接字進行設置。因爲默認情況下最多能包含64個套接字。
下面是一個框架:
- SOCKET s;
- fd_set fread;
- int ret;
- while(TRUE)
- {
- FD_SERO(&fread);
- FD_SET(s,&fread);
- if((ret=select(0,&fread,NULL,NULL,NULL))==SOCKET_ERROR)
- {
- ....
- }
- if(ret>0)
- {
- if(FD_ISSET(s,&fread))
- {
- }
- }
- }
例如,假定我們想知道是否可從一個套接字中安全地讀取數據,同時不會陷於無休止的“鎖
定”狀態,便可使用F D _ S E T宏,將自己的套接字分配給fd_read集合,再來調用select
。要想檢測自己的套接字是否仍屬fd_read集合的一部分,可使用FD_ISSET宏。採用下述步驟
,便可完成用select操作一個或多個套接字句柄的全過程:
1) 使用F D _ Z E R O宏,初始化自己感興趣的每一個 f d _ s e t 。
2) 使用F D _ S E T宏,將套接字句柄分配給自己感興趣的每個 f d _ s e t 。
3) 調用s e l e c t函數,然後等待在指定的f d _ s e t集合中,I / O活動設置好一個或多個套接字句柄。 s e l e c t完成後,會返回在所有f d _ s e t集合中設置的套接字句柄總數,並對每個集合進行相應的更新。
4) 根據s e l e c t 的返回值,我們的應用程序便可判斷出哪些套接字存在着尚未完成(待決) 的I / O操作—具體的方法是使用F D _ I S S E T宏,對每個f d _ s e t集合進行檢查。
5) 知道了每個集合中“待決”的I / O操作之後,對I / O進行處理,然後返回步驟 1 )
,繼續進 行s e l e c t處理。
s e l e c t返回後,它會修改每個 f d _ s e t結構,刪除那些不存在待決 I / O操作的套接字句柄。這正是我們在上述的步驟( 4 ) 中,爲何要使用F D _ I S S E T宏來判斷一個特定的套接字是否仍在集合中的原因。