select的機制中提供了乙個fd_set的資料結構,實際上是long型別的陣列, 每乙個陣列元素都能與一開啟的檔案控制代碼(不管是socket控制代碼,還是其他檔案或命名管道或裝置控制代碼
)建立聯絡,建立聯絡的工作由程式設計師完成,
當呼叫select()時,由核心根據io狀態修改fd_set的內容,由此來通知執行了select()的程序哪一socket或檔案可讀,
下面具體解釋:
int select(nfds, readfds, writefds, exceptfds, timeout)
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
ndfs:需要監視的檔案控制代碼
readfds:select監視的可讀檔案控制代碼集合。
writefds: select監視的可寫檔案控制代碼集合。
exceptfds:select監視的異常檔案控制代碼集合。
timeout:本次select()的超時結束時間。(見/usr/sys/select.h, 可精確至百萬分之一秒!)
當readfds或writefds中映象的檔案可讀或可寫或超時,本次select() 就結束返回。程式設計師利用一組系統提供的巨集在select()結束時便可判 斷哪一檔案可讀或可寫。對socket程式設計特別有用的就是readfds。 幾隻相關的巨集解釋如下:
fd_zero(fd_set *fdset):清空fdset與所有檔案控制代碼的聯絡。
fd_set(int fd, fd_set *fdset):建立檔案控制代碼fd與fdset的聯絡。
fd_clr(int fd, fd_set *fdset):清除檔案控制代碼fd與fdset的聯絡。
fd_isset(int fd, fdset *fdset):檢查fdset聯絡的檔案控制代碼fd是否可讀寫,>0表示可讀寫。
(關於fd_set及相關巨集的定義見/usr/include/sys/types.h)
select 返回值
負值:select錯誤
正值:某些檔案可讀寫或出錯
0:等待超時,沒有可讀寫或錯誤的檔案
具體解釋select的引數:
int maxfdp:是乙個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1,不能錯!在windows中這個引數的值無所謂,可以設定不正確。
fd_set* readfds:是指向fd_set結構的指標,這個集合中應該包括檔案描述符,可以從這些檔案中讀取資料,可以傳入null值,表示不關心任何檔案的讀變化。
fd_set* writefds:是指向fd_set結構的指標,這個集合中應該包括檔案描述符,檔案描述符的寫變化,可以傳入null值,表示不關心任何檔案的寫變化。
fd_set * errorfds:同上面兩個引數的意圖,用來監視檔案錯誤異常
struct timeval* timeout:是select的超時時間,這個引數至關重要,它可以使select處於三種狀態:
第一:若將null以形參傳入,就是將select置於阻塞狀態,一定等到監視檔案描述符集合中某個檔案描述符發生變化為止
第二:若將時間值設為0秒0毫秒,就變成乙個純粹的非阻塞函式,不管檔案描述符是否有變化,都立刻返回繼續執行,檔案無變化返回0,有變化返回乙個正值
第三:timeout的值大於0,這就是等待的超時時間,即select在timeout時間內阻塞,有事件到來就返回了,
fd_accept的實現?依然如上,因為客戶方socket請求連線時,會傳送連線請求報文,此時select()當然會結束,fd_isset(sockfd)當然大於零,因為有報文可讀嘛!至於這方面的應用,主要在於服務方的父socket,你若不喜歡主動accept(),可改為如上機制來accept()。
討論關於利用select()檢測對方socket關閉的問題:
仍然是本地socket有東東可讀,因為對方socket關閉時,會發乙個關閉連線通知報文,會馬上被select()檢測到的。關於tcp的連線(三次握手)和關閉(二次握手)機制,敬請參考有關tcp/ip的書籍。
不知是什麼原因,unix好象沒有提供通知程序關於socket或pipe對方關閉的訊號,也可能是cpu所知有限。總之,當對方關閉,一執行recv()或read(),馬上回返回-1,此時全域性變數errno的值是115
,相應的sys_errlist[errno] 為"connect refused"(請參考/usr/include/sys/errno.h)。所以,在上篇的for(;;)...select()程式塊中,當有東西可讀時,一定要檢查recv()或 read()的返回值,返回-1時要作出關斷本地socket的處理(select有讀是 因為發來的是結束報文嗎?而read卻讀不到這個報文?)
,否則select()會一直認為有東西讀,其結果曾幾令cpu傷心欲斷針腳。不信你可以試試:不檢查recv()返回結果,且將收到的東東(實際沒收到)寫至標準輸出... 在有名管道的程式設計中也有類似問題出現。具體處理詳見拙作:發布乙個有用的socket客戶方原碼。
至於主動寫socket時對方突然關閉的處理則可以簡單地捕捉訊號sigpipe並作出相應關斷本地socket等等的處理。sigpipe的解釋是:寫入無讀者方的管道。 在此不作贅述,請詳man signal。
以上是cpu在作tcp/ip資料傳輸實驗積累的經驗,若有錯漏,請狂炮擊之。
補充關於select在非同步(非阻塞)connect中的應用,剛開始搞socket程式設計的時候 我一直都用阻塞式的connect,
用select可以很好地解決這一問題.大致過程是這樣的:
1.將開啟的socket設為非阻塞的,可以用fcntl(socket, f_setfl, o_ndelay)完成(有的系統用fnedlay也可).
2.發connect呼叫,這時返回-1,但是errno被設為einprogress,意即connect仍舊在進行還沒有完成.
3.將開啟的socket設進被監視的可寫(注意不是可讀)檔案集合用select進行監視,如果可寫,用
getsockopt(socket, sol_socket, so_error, &error, sizeof(int));
來得到error的值,如果為零,則connect成功.
在有了select後可以寫出像樣的網路程式來!舉個簡單的例子,就是從網路上接受資料寫入乙個檔案中。
main()
; //select等待3秒,3秒輪詢,要非阻塞就置0
........
while(1) }
select 函式詳解
select函式 系統提供select函式來實現多路復用輸入 輸出模型。原型 include include select函式 系統提供select函式來實現多路復用輸入 輸出模型。原型 include include int select int maxfd,fd set rdset,fd set...
select函式詳解
select 在socket程式設計中還是比較重要的,可是對於初學socket的人來說都不太愛用select寫程式,他們只是習慣寫諸如connect accept recv或recvfrom這樣的阻塞程式 所謂阻塞方式block,顧名思義,就是程序或是執行緒執行到這些函式時必須等待某個事件的發生,如...
select函式詳解
select函式允許程序指示核心等待多個事件中的任何乙個發生,並只在有乙個或多個事件發生或經歷一段指定時間後才喚醒它。通常我們呼叫select告知核心對哪些描述符 就讀 寫或異常條件 感興趣以及等待多長時間。不侷限於套接字,任何描述符都可以使用select來測試。當然,我們最熟悉的描述符是0 1 2...