二、主要擴充說明
1、異步選擇機制:
Windows SOCKETS 的異步選擇函數提供了消息機制的網絡事件選擇,當使用它登記網絡事件發生時,應用程序相應窗口函數將收到一個消息,消息中指示了發生的網絡事件,以及與事件相關的一些信息。
Windows SOCKETS 提供了一個異步選擇函數 WSAAsyncSelect(),用它來注冊應用程序感興趣的網絡事件,當這些事件發生時,應用程序相應的窗口函數將收到一個消息。
函數結構如下:
int PASCAL FAR WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);參數說明:
rc=WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);如果我們需要注銷對套接字網絡事件的消息發送,只要將 lEvent 設置為0
3、阻塞處理方法
WINDOWS SOCKETS 為了實現當一個應用程序的套接字調用處於阻塞時,能夠放棄CPU讓其它應用程序運行,它在調用處於阻塞時便進入一個叫“HOOK”的例程,此例程負責接收和分配Windows消息,使得其它應用程序仍然能夠接收到自己的消息並取得控制權。
WINDOWS 是非搶先的多任務環境,即若一個程序不主動放棄其控制權,別的程序就不能執行。因此在設計 WINDOWS SOCKETS 程序時,盡管系統支持阻塞操作,但還是反對程序員使用該操作。但由於 SUN 公司下的 Berkeley Sockets 的套接字默認操作是阻塞的,Windows 作為移植的 SOCKETS 也不可避免對這個操作支持。
在 WINDOWS SOCKETS 實現中,對於不能立即完成的阻塞操作做如下處理:DLL初始化→循環操作。在循環中,它發送任何 WINDOWS 消息,並檢查這個 Windows SOCKETS 調用是否完成,在必要時,它可以放棄CPU讓其它應用程序執行(當然使用超線程的CPU就不會有這個麻煩了^_^)。我們可以調用 WSACancelBlockingCall() 函數取消此阻塞操作。
在 WINDOWS SOCKETS 中,有一個默認的阻塞處理例程 BlockingHook() 簡單地獲取並發送 WINDOWS 消息。如果要對復雜程序進行處理,WINDOWS SOCKETS 中還有 WSASetBlockingHook() 提供用戶安裝自己的阻塞處理例程能力;與該函數相對應的則是 SWAUnhookBlockingHook(),它用於刪除先前安裝的任何阻塞處理例程,並重新安裝默認的處理例程。請注意,設計自己的阻塞處理例程時,除了函數 WSACancelBlockingHook() 之外,它不能使用其它的 Windows SOCKETS API 函數。在處理例程中調用 WSACancelBlockingHook()函數將取消處於阻塞的操作,它將結束阻塞循環。
4、出錯處理
WINDOWS SOCKETS 為了和以後多線程環境(Windows/UNIX)兼容,它提供了兩個出錯處理函數來獲取和設置當前線程的最近錯誤號。(WSAGetLastEror()和WSASetLastError())
5、啟動與終止
使用函數 WSAStartup() 和 WSACleanup() 啟動和終止套接字。
int PASCAL FAR WSAStartup(Word wVersionRequested, LPWSADATA lpWSAData);其中 wVersionRequested 保證 SOCKETS 可正常運行的 DLL 版本,如果不支持,則返回錯誤信息。
WORD wVersionRequested;// 定義版本信息變量 WSADATA wsaData;//定義數據信息變量 int err;//定義錯誤號變量 wVersionRequested = MAKEWord(1,1);//給版本信息賦值 err = WSAStartup(wVersionRequested, &wsaData);//給錯誤信息賦值 if(err!=0) { return;//告訴用戶找不到合適的版本 } //確認 WINDOWS SOCKETS DLL 支持 1.1 版本 //DLL 版本可以高於 1.1 //系統返回的版本號始終是最低要求的 1.1,即應用程序與DLL 中可支持的最低版本號 if(LOBYTE(wsaData.wVersion)!= 1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup();//告訴用戶找不到合適的版本 return; } //Windows SOCKETS DLL 被進程接受,可以進入下一步操作關閉函數使用時,任何打開並已連接的 SOCK_STREAM 套接字被復位,但那些已由 closesocket() 函數關閉的但仍有未發送數據的套接字不受影響,未發送的數據仍將被發送。程序運行時可能會多次調用 WSAStartuo() 函數,但必須保證每次調用時的 wVersionRequested 的值是相同的。
HANDLE taskHnd; char hostname="rs6000"; taskHnd = WSAAsyncBetHostByName(hWnd,wMsg,hostname,buf,buflen);需要注意的是,由於 Windows 的內存對像可以設置為可移動和可丟棄,因此在操作內存對象是,必須保證 WIindows Sockets DLL 對象是可用的。
3、異步數據傳輸
使用 send() 或 sendto() 函數來發送數據,使用 recv() 或recvfrom() 來接收數據。Windows Sockets 不鼓勵用戶使用阻塞方式傳輸數據,因為那樣可能會阻塞整個 Windows 環境。下面我們看一個異步數據傳輸實例:
假設套接字 s 在連接建立後,已經使用了函數 WSAAsyncSelect() 在其上注冊了網絡事件 FD_READ 和 FD_WRITE,並且 wMsg 值為 UM_SOCK,那麼我們可以在 Windows 消息循環中增加如下的分支語句:
case UM_SOCK: switch(lParam) { case FD_READ: len = recv(wParam,lpBuffer,length,0); break; case FD_WRITE: while(send(wParam,lpBuffer,len,0)!=SOCKET_ERROR) break; } break;4、出錯處理
len = send (s,lpBuffer,len,0); of((len==SOCKET_ERROR)&&(WSAGetLastError()==WSAWOULDBLOCK)){...}