1. 程式人生 > >設置非阻塞的套接字Socket

設置非阻塞的套接字Socket

api pad def while循環 ace 使用方法 小時 acc 使用

當使用socket()函數和WSASocket()函數創建套接字時,默認都是阻塞的。在創建套接字之後,通過調用ioctlsocket()函數,將該套接字設置為非阻塞模式。函數的第一個參數是套接字,第二個參數設置為FIONBIO,第三個參數設置為unsigned long類型的非零值。下面代碼清單演示了如何用ioctlsocket()函數,設置套接字為非阻塞模式。

SOCKET s; //套接字

unsigned long ul = 1; //設置套接字選項

int nRet; //返回值

s = socket(AF_INET, SOCK_STREAM, 0); //創建套接字

nRet = ioctlsocket(s, FIONBIO, (unsigned long*)&ul); //設置套接字非阻塞模式

if (nRet == SOCKET_ERROR)

{

//設置套接字非阻塞模式,失敗處理

}

套接字設置為非阻塞模式後,在調用Windows Sockets API函數時,調用函數會立即返回。大多數情況下,這些函數調用都會調用“失敗”,並返回WSAEWOULDBLOCK錯誤代碼。說明請求的操作在調用期間內沒有時間完成。通常,應用程序需要重復調用該函數,直到獲得成功返回代碼。下面程序清單示例了一個在非阻塞套接字上反復調用recv()函數,直到收到1024個字節的數據。

#define NUM_REQUIRED 1024 //需要讀入數據的大小

#define MAX_SIZE 2048 //輸入緩沖區的大小

TCHAR buff[MAX_SIZE]; //輸入緩沖區

bool close; //對方關閉了連接

SOCKET sock; //Windows sockets

void ReadData(void)

{

int nTotal = 0; //已經讀入緩沖區字節數

int nRead = 0; //在調用recv時實際讀入字節數

int nLeft = 0; //剩下數據的字節數

int nBytes = 0; //當前已讀數據在緩沖區的位置

nLeft = NUM_REQUIRED;

while (nTotal != NUM_REQUIRED)//已經讀入緩沖區的字節數不等於需要讀入的大小時

{

nRead = recv(sock, &buff[MAX_SIZE - nBytes], nLeft, 0); //接收數據

if(SOCKET_ERROR == nRead) //讀操作失敗

{

int err = WSAGetLastError();

if(WSAEWOULDBLOCK == err) //接收數據緩沖區不可用

{

continue; //接著讀取數據

}else if(WSAETIMEDOUT == err || WSAENETDOWN == err) //連接已經斷開

{

close = TRUE; //函數退出

break;

}

}

if( 0 == nRead) //客戶端關閉了連接

{

close = TRUE; //函數退出

break;

}

nTotal += nRead;

nLeft -= nRead;

nBytes += nRead;

}

return;

}

在該程序中,通過調用WSAGetLastError()函數獲得recv()函數返回的錯誤代碼。當返回WSAEWOULDBLOCK錯誤時,說明此時套接字的緩沖區還沒有準備好的數據。需要繼續調用該函數。

在該程序中,還對recv()函數返回的其他錯誤代碼進行處理。WSAETIMEDOUT和WSAENETDOWN錯誤說明,此時由於網絡系統的原因與對方的連接已經斷開了。當函數返回0時,說明對方關閉了連接。在程序中通過設置close布爾變量值為TRUE,表明與對方的連接已經斷開。調用break語句跳出while循環體,函數退出。在開發中,應該根據具體情況對函數返回的錯誤值進行具體處理。

不同的Windows Sockets API函數,在調用失敗時返回的WSAEWOULDBLOCK錯誤代碼具有不同的含義。表對幾個Windows Sockets API函數返回WSAEWOULDBLOCK錯誤的含義進行了總結。

表 WSAEWOULDBLOCK的含義

函數名

說明

accept()和WSAAcept()

應用程序沒有收到連接請求

recv()、WSARecv()、recvfrom()和WSARecvfrom()

接收緩沖區沒有收到數據

send()、WSASend()、sendfrom()和WSASendfrom()

發送緩沖區此時不可用

connect()和WSAConnect()

連接未能立即完成

closescoket()

通常情況下意味著應用程序使用SO_LINGER選項並且設置了一個非零的超時值,調用了setsocketopt()函數

需要說明的是並非所有的Windows Sockets API在非阻塞模式下調用,都會返回WSAEWOULDBLOCK錯誤。例如,以非阻塞模式的套接字為參數調用bind()函數時,就不會返回該錯誤代碼。當然,在調用WSAStartup()函數時更不會返回該錯誤代碼,因為該函數是應用程序第一調用的函數,當然不會返回這樣的錯誤代碼。

要將套接字設置為非阻塞模式,除了使用ioctlsocket()函數之外,還可以使用WSAAsyncselect()和WSAEventselect()函數。當調用該函數時,套接字會自動地設置為非阻塞方式。在後續章節中,講解該函數的使用方法。

設置非阻塞的套接字Socket