把套接字設置為非阻塞模式,即通知系統(tǒng)內核:在調用Windows Sockets API時,不要讓線程睡眠,而應該讓函數(shù)立即返回。在返回時,該函數(shù)返回一個錯誤代碼。圖所示,一個非阻塞模式套接字多次調用recv()函數(shù)的過程。前三次調用recv()函數(shù)時,內核數(shù)據(jù)還沒有準備好。因此,該函數(shù)立即返回WSAEWOULDBLOCK錯誤代碼。第四次調用recv()函數(shù)時,數(shù)據(jù)已經(jīng)準備好,被復制到應用程序的緩沖區(qū)中,recv()函數(shù)返回成功指示,應用程序開始處理數(shù)據(jù)。 當使用socket()函數(shù)和WSASocket()函數(shù)創(chuàng)建套接字時,默認都是阻塞的。在創(chuàng)建套接字之后,通過調用ioctlsocket()函數(shù),將該套接字設置為非阻塞模式。函數(shù)的第一個參數(shù)是套接字,第二個參數(shù)設置為FIONBIO,第三個參數(shù)設置為unsigned long類型的非零值。下面代碼清單演示了如何用ioctlsocket()函數(shù),設置套接字為非阻塞模式。 SOCKET s; //套接字 unsigned long ul = 1; //設置套接字選項 int nRet; //返回值 s = socket(AF_INET, SOCK_STREAM, 0); //創(chuàng)建套接字 nRet = ioctlsocket(s, FIONBIO, (unsigned long*)&ul); //設置套接字非阻塞模式 if (nRet == SOCKET_ERROR) { //設置套接字非阻塞模式,失敗處理 } 套接字設置為非阻塞模式后,在調用Windows Sockets API函數(shù)時,調用函數(shù)會立即返回。大多數(shù)情況下,這些函數(shù)調用都會調用“失敗”,并返回WSAEWOULDBLOCK錯誤代碼。說明請求的操作在調用期間內沒有時間完成。通常,應用程序需要重復調用該函數(shù),直到獲得成功返回代碼。下面程序清單示例了一個在非阻塞套接字上反復調用recv()函數(shù),直到收到1024個字節(jié)的數(shù)據(jù)。 #define NUM_REQUIRED 1024 //需要讀入數(shù)據(jù)的大小 #define MAX_SIZE 2048 //輸入緩沖區(qū)的大小 TCHAR buff[MAX_SIZE]; //輸入緩沖區(qū) bool close; //對方關閉了連接 SOCKET sock; //Windows sockets void ReadData(void) { int nTotal = 0; //已經(jīng)讀入緩沖區(qū)字節(jié)數(shù) int nRead = 0; //在調用recv時實際讀入字節(jié)數(shù) int nLeft = 0; //剩下數(shù)據(jù)的字節(jié)數(shù) int nBytes = 0; //當前已讀數(shù)據(jù)在緩沖區(qū)的位置
nLeft = NUM_REQUIRED; while (nTotal != NUM_REQUIRED)//已經(jīng)讀入緩沖區(qū)的字節(jié)數(shù)不等于需要讀入的大小時 { nRead = recv(sock, &buff[MAX_SIZE - nBytes], nLeft, 0); //接收數(shù)據(jù)
if(SOCKET_ERROR == nRead) //讀操作失敗 { int err = WSAGetLastError(); if(WSAEWOULDBLOCK == err) //接收數(shù)據(jù)緩沖區(qū)不可用 { continue; //接著讀取數(shù)據(jù) }else if(WSAETIMEDOUT == err || WSAENETDOWN == err) //連接已經(jīng)斷開 { close = TRUE; //函數(shù)退出 break; } }
if( 0 == nRead) //客戶端關閉了連接 { close = TRUE; //函數(shù)退出 break; }
nTotal += nRead; nLeft -= nRead; nBytes += nRead; } return; } 在該程序中,通過調用WSAGetLastError()函數(shù)獲得recv()函數(shù)返回的錯誤代碼。當返回WSAEWOULDBLOCK錯誤時,說明此時套接字的緩沖區(qū)還沒有準備好的數(shù)據(jù)。需要繼續(xù)調用該函數(shù)。 在該程序中,還對recv()函數(shù)返回的其他錯誤代碼進行處理。WSAETIMEDOUT和WSAENETDOWN錯誤說明,此時由于網(wǎng)絡系統(tǒng)的原因與對方的連接已經(jīng)斷開了。當函數(shù)返回0時,說明對方關閉了連接。在程序中通過設置close布爾變量值為TRUE,表明與對方的連接已經(jīng)斷開。調用break語句跳出while循環(huán)體,函數(shù)退出。在開發(fā)中,應該根據(jù)具體情況對函數(shù)返回的錯誤值進行具體處理。 不同的Windows Sockets API函數(shù),在調用失敗時返回的WSAEWOULDBLOCK錯誤代碼具有不同的含義。表對幾個Windows Sockets API函數(shù)返回WSAEWOULDBLOCK錯誤的含義進行了總結。 表 WSAEWOULDBLOCK的含義
需要說明的是并非所有的Windows Sockets API在非阻塞模式下調用,都會返回WSAEWOULDBLOCK錯誤。例如,以非阻塞模式的套接字為參數(shù)調用bind()函數(shù)時,就不會返回該錯誤代碼。當然,在調用WSAStartup()函數(shù)時更不會返回該錯誤代碼,因為該函數(shù)是應用程序第一調用的函數(shù),當然不會返回這樣的錯誤代碼。 要將套接字設置為非阻塞模式,除了使用ioctlsocket()函數(shù)之外,還可以使用WSAAsyncselect()和WSAEventselect()函數(shù)。當調用該函數(shù)時,套接字會自動地設置為非阻塞方式。在后續(xù)章節(jié)中,講解該函數(shù)的使用方法。 |
|
來自: 通信小兵 > 《socket通信》