Windows Socket 異步編程(非阻塞模式)

1. Windows平臺提供了5種非阻塞Socket編程模型:

Select模型(集合管理多個Socket,集合中有64個元素,可以管理1024個socket)

WSAAsyncSelect模型(消息通知應用程序)

WSAEventSelect模型(事件通知應用程序)

每次只能等待64個事件

重疊I/O模型(Overlapped I/O,事件通知和完成例程來通知應用程序)

事件通知即通過事件來通知應用程序I/O操作已完成,完成例程則是預先定義的回調函數。

完成端口模型(Completion Port)

比較成熟,使用線程池來處理異步I/O請求

2. ioctlsocket

int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp);

s:一個標識套接口的描述字。

cmd:對套接口s的操作命令。

argp:指向cmd命令所帶參數的指針。
 
 
cmd參數可選值:

FIONBIO:

  允許或禁止套接口s的非阻塞模式。argp指向一個無符號長整型。如允許非阻塞模式則非零,如禁止非阻塞模式則爲零。

FIONREAD:

  確定套接口s自動讀入的數據量。argp指向一個無符號長整型,其中存有ioctlsocket()的返回值。如果s是SOCKET_STREAM類型,則FIONREAD返回在一次recv()中所接收的所有數據量,這通常與套接口中排隊的數據總量相同。如果s是SOCK_DGRAM 型,則FIONREAD返回套接口上排隊的第一個數據報大小。

SIOCATMARK:

  確實是否所有的帶外數據都已被讀入。這個命令僅適用於SOCK_STREAM類型的套接口,且該套接口已被設置爲可以在線接收帶外數據(SO_OOBINLINE)。如無帶外數據等待讀入,則該操作返回TRUE真。否則的話返回FALSE假,下一個recv()或recvfrom()操作將檢索“標記”前一些或所有數據。應用程序可用SIOCATMARK操作來確定是否有數據剩下。如果在“緊急”(帶外)數據[前有常規數據,則按序接收這些數據(請注意,recv()和recvfrom()操作不會在一次調用中混淆常規數據與帶外數]據)。argp指向一個BOOL型數,ioctlsocket()在其中存入返回值。
 
返回值:
成功後,ioctlsocket()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程序可通過WSAGetLastError()獲取相應錯誤代碼。
錯誤代碼:
WSANOTINITIALISED:在使用此API之前應首先成功地調用WSAStartup()。
WSAENETDOWN:WINDOWS套接口實現檢測到網絡子系統失效。
WSAEINVAL:cmd爲非法命令,或者argp所指參數不適用於該cmd命令,或者該命令不適用於此種類型的套接口。
WSAEINPROGRESS:一個阻塞的WINDOWS套接口調用正在運行中。
WSAENOTSOCK:描述字不是一個套接口。

3. Select

int select(
  int nfds,
  fd_set FAR* readfds,
  fd_set FAR* writefds,
  fd_set FAR* exceptfds,
  const struct timeval FAR* timeout
);

nfds: 本參數忽略,僅起到兼容作用。

fd_set* readfds: 是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的讀變化的,即我們關心是否可以從這些文件中讀取數據了,如果這個集合中有一個文件可讀,select就會返回一個大於0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的讀變化。

fd_set*writefds: 是指向fd_set結構的指針,這個集合中應該包括文件描述符,我們是要監視這些文件描述符的寫變化的,即我們關心是否可以向這些文件中寫入數據了,如果這個集合中有一個文件可寫,select就會返回一個大於0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入NULL值,表示不關心任何文件的寫變化。

fd_set *errorfds: 同上面兩個參數的意圖,用來監視文件錯誤異常。

struct timeval *timeout: 是select的超時時間,這個參數至關重要,它可以使select處於三種狀態,第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化爲止;第二,若將時間值設爲0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大於0,這就是等待的超時時間,即select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。

structtimeval{
  long tv_sec;//seconds
  long tv_usec;//mill seconds
};

返回值:

  負值:select錯誤

  正值:某些文件可讀寫或出錯

  0:等待超時,沒有可讀寫或錯誤的文件

 

對socket 和 fd_set集合的操作

FD_SET(int fd, fd_set *fdset);    //向集合中添加socket s

FD_CLR(int fd, fd_set *fdset);    //從集合中刪除指定的socket s

FD_ISSET(int fd, fd_set *fdset);   //如果s是結合中的成員,則返回非0,否則返回0

FD_ZERO(fd_set *fdset);        //將集合初始化爲空集合

4. WSAFunc

SOCKET WSASocket(
  __in  int af,
  __in  int type,
  __in  int protocol,
  __in  LPWSAPROTOCOL_INFO lpProtocolInfo,
  __in  GROUP g,
  __in  DWORD dwFlags
);

dwFlags:

WSA_FLAG_OVERLAPPEDCreate a socket that supports overlapped I/O operations.

Most sockets should be created with this flag set. Overlapped sockets can utilize 

WSASendWSASendToWSARecvWSARecvFrom, andWSAIoctl for

overlapped I/O operations, which allow multiple operations to be initiated

and in progress simultaneously.

All functions that allow overlapped operation (WSASendWSARecvWSASendToWSARecvFromWSAIoctl)also support nonoverlapped usage on an overlapped socket if the values for parameters related to overlapped operations are NULL.

 

 (更加詳細的解釋,請參看MSDN http://msdn.microsoft.com/en-us/library/ms742212(v=vs.85).aspx)

示例代碼:

ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 

 
int WSASend(
  __in   SOCKET s,
  __in   LPWSABUF lpBuffers,
  __in   DWORD dwBufferCount,
  __out  LPDWORD lpNumberOfBytesSent,
  __in   DWORD dwFlags,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
   
 
int WSASendTo(
  __in   SOCKET s,
  __in   LPWSABUF lpBuffers,
  __in   DWORD dwBufferCount,
  __out  LPDWORD lpNumberOfBytesSent,
  __in   DWORD dwFlags,
  __in   const struct sockaddr *lpTo,
  __in   int iToLen,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
   
 
int WSARecv(
  __in     SOCKET s,
  __inout  LPWSABUF lpBuffers,
  __in     DWORD dwBufferCount,
  __out    LPDWORD lpNumberOfBytesRecvd,
  __inout  LPDWORD lpFlags,
  __in     LPWSAOVERLAPPED lpOverlapped,
  __in     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
   
 
int WSARecvFrom(
  __in     SOCKET s,
  __inout  LPWSABUF lpBuffers,
  __in     DWORD dwBufferCount,
  __out    LPDWORD lpNumberOfBytesRecvd,
  __inout  LPDWORD lpFlags,
  __out    struct sockaddr *lpFrom,
  __inout  LPINT lpFromlen,
  __in     LPWSAOVERLAPPED lpOverlapped,
  __in     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
   
 
int WSAIoctl(
  __in   SOCKET s,
  __in   DWORD dwIoControlCode,
  __in   LPVOID lpvInBuffer,
  __in   DWORD cbInBuffer,
  __out  LPVOID lpvOutBuffer,
  __in   DWORD cbOutBuffer,
  __out  LPDWORD lpcbBytesReturned,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
   

 

5. WSAAsyncSelect(具體參照MSDN)

int WSAAsyncSelect(
  __in  SOCKET s,
  __in  HWND hWnd,
  __in  unsigned int wMsg,
  __in  long lEvent
);
  s 標識一個需要事件通知的套接口的描述符.
  hWnd 標識一個在網絡事件發生時需要接收消息的窗口句柄.
  wMsg 在網絡事件發生時要接收的消息.
  lEvent 位屏蔽碼,用於指明應用程序感興趣的網絡事件集合.
注: WSAAsyncSelect()函數會自動將socket s設置爲非阻塞模式。
缺點:需要窗口環境

6. WSAEventSelect

int WSAEventSelect(
  __in  SOCKET s,
  __in  WSAEVENT hEventObject,
  __in  long lNetworkEvents
);
s: A descriptor identifying the socket.
hEventObject: A handle identifying the event object to be associated with the specified set of FD_XXX network events.
lNetworkEvents: A bitmask that specifies the combination of FD_XXX network events in which the application has interest.

WSAEventSelect自動將socket s設置爲非阻塞模式。
缺點:每次只能等待64個事件,但不像Async那樣需要窗口

7. WSAEventSelect中涉及到的函數

WSAEVENT WSACreateEvent(void)
創建事件對象
BOOL WSAResetEvent(
  __in  WSAEVENT hEvent
)
將事件對象設置爲無信號狀態
BOOL WSASetEvent(
  __in  WSAEVENT hEvent
)
將事件對象設置有無信號狀態
BOOL WSACloseEvent(
  __in  WSAEVENT hEvent
)
關閉事件對象
DWORD WSAWaitForMultipleEvents(
  __in  DWORD cEvents,
  __in  const WSAEVENT *lphEvents,
  __in  BOOL fWaitAll,
  __in  DWORD dwTimeout,
  __in  BOOL fAlertable
)

DWORD cEvents:等候事件的總數量

const WSAEVENT* lphEvents:事件數組的指針

BOOL fWaitAll:如果設置爲 TRUE,則事件數組中所有事件被傳信的時候函數纔會返回,FALSE則任何一個事件被傳信函數都要返回

DWORD dwTimeout:如果設置爲0,函數會立即返回, 如果設置爲 WSA_INFINITE只有滿足參數fWaitAll指定的條件纔會返回

BOOL fAlertable:主要用於重疊I/O模型

 
int WSAEnumNetworkEvents(
  __in   SOCKET s,
  __in   WSAEVENT hEventObject,
  __out  LPWSANETWORKEVENTS lpNetworkEvents
)

s: A descriptor identifying the socket.

hEventObject: An optional handle identifying an associated event object to be reset.

lpNetworkEvents: A pointer to a WSANETWORKEVENTS structure that is filled with a record of network events that occurred and any associated error codes.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章