WinSock基本知識
這裡不打算系統地介紹socket或者WinSock的知識。首先介紹WinSock API函數,講解阻塞/非阻塞的概念;然後介紹socket的使用。
WinSock API
Socket接口是網絡編程(通常是TCP/IP協議,也可以是其他協議)的API。最早的Socket接口是Berkeley接口,在Unxi操作系統中實現。WinSock也是一個基於Socket模型的API,在Microsoft Windows操作系統類中使用。它在Berkeley接口函數的基礎之上,還增加了基於消息驅動機制的Windows擴展函數。Winscok1.1只支持TCP/IP網絡,WinSock2.0增加了對更多協議的支持。這裡,討論TCP/IP網絡上的API。
Socket接口包括三類函數:
第一類是WinSock API包含的Berkeley socket函數。這類函數分兩部分。第一部分是用於網絡I/O的函數,如
accept、Closesocket、connect、recv、recvfrom、Select、Send、Sendto
另一部分是不涉及網絡I/O、在本地端完成的函數,如
bind、getpeername、getsockname、getsocketopt、htonl、htons、inet_addr、inet_nton
ioctlsocket、listen、ntohl、ntohs、setsocketopt、shutdow、socket等
第二類是檢索有關域名、通信服務和協議等Internet信息的數據庫函數,如
gethostbyaddr、gethostbyname、gethostname、getprotolbyname
getprotolbynumber、getserverbyname、getservbyport。
第三類是Berkekley socket例程的Windows專用的擴展函數,如gethostbyname對應的WSAAsynGetHostByName(其他數據庫函數除了gethostname都有異步版本),select對應的WSAAsynSelect,判斷是否阻塞的函數WSAIsBlocking,得到上一次Windsock API錯誤信息的WSAGetLastError,等等。
從另外一個角度,這些函數又可以分為兩類,一是阻塞函數,一是非阻塞函數。所謂阻塞函數,是指其完成指定的任務之前不允許程序調用另一個函數,在Windows下還會阻塞本線程消息的發送。所謂非阻塞函數,是指操作啟動之後,如果可以立即得到結果就返回結果,否則返回表示結果需要等待的錯誤信息,不等待任務完成函數就返回。
首先,異步函數是非阻塞函數;
其次,獲取遠地信息的數據庫函數是阻塞函數(因此,WinSock提供了其異步版本);
在Berkeley socket函數部分中,不涉及網絡I/O、本地端工作的函數是非阻塞函數;
在Berkeley socket函數部分中,網絡I/O的函數是可阻塞函數,也就是它們可以阻塞執行,也可以不阻塞執行。這些函數都使用了一個socket,如果它們使用的socket是阻塞的,則這些函數是阻塞函數;如果它們使用的socket是非阻塞的,則這些函數是非阻塞函數。
創建一個socket時,可以指定它是否阻塞。在缺省情況下,Berkerley的Socket函數和WinSock都創建“阻塞”的socket。阻塞socket通過使用select函數或者WSAAsynSelect函數在指定操作下變成非阻塞的。WSAAsyncSelect函數原型如下。
int WSAAsyncSelect(
SOCKET s,
HWND hWnd,
u_int wMsg,
long lEvent
);
其中,參數1指定了要操作的socket句柄;參數2指定了一個窗口句柄;參數3指定了一個消息,參數4指定了網絡事件,可以是多個事件的組合,如:
FD_READ 准備讀
FD_WRITE 准備寫
FD_OOB 帶外數據到達
FD_ACCEPT 收到連接
FD_CONNECT 完成連接
FD_CLOSE 關閉socket。
用OR操作組合這些事件值,如FD_READ|FD_WRITE
WSAAsyncSelect函數表示對socket s監測lEvent指定的網絡事件,如果有事件發生,則給窗口hWnd發送消息wMsg。
假定應用程序的一個socket s指定了監測FD_READ事件,則在FD_READ事件上變成非阻塞的。當read函數被調用時,不管是否讀到數據都馬上返回,如果返回一個錯誤信息表示還在等待,則在等待的數據到達後,消息wMsg發送給窗口hWnd,應用程序處理該消息讀取網絡數據。
對於異步函數的調用,以類似的過程最終得到結果數據。以gethostbyname的異步版本的使用為例進行說明。該函數原型如下: