設置非阻塞的套接字Socket


當使用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()函數。當調用該函數時,套接字會自動地設置為非阻塞方式。在后續章節中,講解該函數的使用方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM