1. 程式人生 > >網路程式設計中重要函式總結:如何判斷socket關閉

網路程式設計中重要函式總結:如何判斷socket關閉

1、阻塞模式與非阻塞模式下recv的返回值各代表什麼意思?有沒有區別?(就我目前瞭解阻塞與非阻塞recv返回值沒有區分,都是 <0:出錯,=0:連線關閉,>0接收到資料大小,特別:返回值 <0時並且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情況下認為連線是正常的,繼續接收。只是阻塞模式下recv會阻塞著接收資料,非阻塞模式下如果沒有資料會返回,不會阻塞著讀,因此需要迴圈讀取)。

2、阻塞模式與非阻塞模式下write的返回值各代表什麼意思?有沒有區別?(就我目前瞭解阻塞與非阻塞write返回值沒有區分,都是 <0:出錯,=0:連線關閉,>0傳送資料大小,特別:返回值 <0時並且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情況下認為連線是正常的,繼續傳送。只是阻塞模式下write會阻塞著傳送資料,非阻塞模式下如果暫時無法傳送資料會返回,不會阻塞著write,因此需要迴圈傳送)。

3、阻塞模式下read返回值 < 0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN時,連線異常,需要關閉,read返回值 < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)時表示沒有資料,需要繼續接收,如果返回值大於0表示接送到資料。 
非阻塞模式下read返回值 < 0表示沒有資料,= 0表示連線斷開,> 0表示接收到資料。 
這2種模式下的返回值是不是這麼理解,有沒有跟詳細的理解或跟準確的說明?

4、阻塞模式與非阻塞模式下是否send返回值 < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)表示暫時傳送失敗,需要重試,如果send返回值 <= 0, && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN時,連線異常,需要關閉,如果send返回值 > 0則表示傳送了資料?send的返回值是否這麼理解,阻塞模式與非阻塞模式下send返回值=0是否都是傳送失敗,還是那個模式下表示暫時不可傳送,需要重發?

5、很多人說阻塞模式下read會阻塞著讀,是否這樣?我和同事試了不會阻塞read。

6、網路上找了很多資料,說的都很籠統,就分大於0,小於0,等於0,並沒有區分阻塞與非阻塞,更沒有區分一個錯誤號,希望哪位高手能按上面的問題逐條回答一下,越詳細越好,平時少上CSDN,分少,見諒。 
select():

select()的機制中提供一fd_set的資料結構,實際上是一long型別的陣列,每一個數組元素都能與一開啟的

檔案控制代碼(不管是Socket控制代碼,還是其他 檔案或命名管道或裝置控制代碼)建立聯絡,建立聯絡的工作由程式設計師

完成,當呼叫select()時,由核心根據IO狀態修改fd_set的內容,由此來通知執行了select()的程序哪一

Socket或檔案可讀,下面具體解釋:

int select(nfds, readfds, writefds, exceptfds, timeout)

int nfds;

fd_set *readfds, *writefds, *exceptfds;

struct timeval *timeout;

ndfs:select監視的檔案控制代碼數,視程序中開啟的檔案數而定,一般設為你要監視各檔案中的最大檔案號

加一。

readfds:select監視的可讀檔案控制代碼集合。

writefds: select監視的可寫檔案控制代碼集合。

exceptfds:select監視的異常檔案控制代碼集合

timeout:本次select()的超時結束時間。(見/usr/include/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)

這樣,你的socket只需在有東東讀的時候才讀入,大致如下: 
... 
int sockfd; 
fd_set fdR; 
struct timeval timeout = ..; 
... 
for(;;) { 
    FD_ZERO(&fdR); 
    FD_SET(sockfd, &fdR); 
    switch (select(sockfd + 1, &fdR, NULL, &timeout)) { 
        case -1: 
            error handled by u; 
        case 0: 
            timeout hanled by u; 
        default: 
            if (FD_ISSET(sockfd)) { 
            now u read or recv something; 
            /* if sockfd is father and server socket, u can now accept() */ 
            } 
    } 
}

所以一個FD_ISSET(sockfd)就相當通知了sockfd可讀。

至於struct timeval在此的功能,請man select。不同的timeval設定 使使select()表現出超時結束、

無超時阻塞和輪詢三種特性。由於timeval可精確至百萬分之一秒,所以Windows的SetTimer()根本不算什麼。你可以用select()做一個超級時鐘