IOCP 系列函式講解

xianjian_x發表於2016-05-14

CreateIoCompletionPort()詳解

函式原型:

HANDLE WINAPI CreateIoCompletionPort(
  __in          HANDLE FileHandle,
  __in          HANDLE ExistingCompletionPort,
  __in          ULONG_PTR CompletionKey,
  __in          DWORD NumberOfConcurrentThreads
);

  一個I/O完成埠關聯一個或多個檔案控制程式碼,也可以在建立I/O完成埠的時候 沒有關聯任何檔案控制程式碼。一個I/O完成埠關聯一個開啟檔案的例項使應用程式可以收到包括這個檔案非同步I/O操作的完成通知。

引數:
FileHandle 開啟重疊IO完成埠的檔案控制程式碼
如果設定這個引數為INVALID_HANDLE_VALUE,那 麼,CreateIoCompletionPort 會建立一個不關聯任何檔案的完成埠,而且ExistingCompletionPort 必須設定為NULL,CompletionKey 也將被忽略。

ExistingCompletionPort 完成埠控制程式碼
如果指定一個已經存在的完成埠,函式將關聯 FileHandle 指定的檔案,並返回已存在的完成埠控制程式碼,函式不會建立一個新的完成埠。
如果這個引數為NULL,函式建立一個與FileHandle指定的文 件關聯的完成埠,並返回一個新的完成埠控制程式碼。

CompletionKey 單檔案控制程式碼,包含指定檔案每次IO完成包資料資訊。

NumberOfConcurrentThreads 系統允許在完成埠上併發處理IO完成包的最大執行緒數量。如果 ExistingCompletionPort 為NULL,此引數忽略

返回值:
如果執行成功,函式返回關聯指定檔案的完成埠控制程式碼;否則,返回NULL

備註:
  CreateIoCompletionPort 提供這個功能:I/O系統可以被用來向列隊的I/O完成埠傳送I/O完成通知包。當 你執行一個已經關聯一個完成埠的檔案I/O操作,I/O系統將會在這個I/O操作完成的時候向I/O完成埠傳送一個完成通知包,I/O完成埠將以先 進先出的方式放置這個I/O完成通知包,並使用GetQueuedCompletionStatus 接收I/O完成通知包。
  雖然允許任何數量的 執行緒來呼叫 GetQueuedCompletionStatus 等待一個I/O完成埠,但每個執行緒只能同時間內關聯一個I/O完成埠,且此埠是執行緒最後檢查的那個埠。
  當一個包被放入佇列中,系統首先會 檢查有多少個關聯此埠的執行緒在執行,如果執行的執行緒的數量少於NumberOfConcurrentThreads的值,那麼允許其中的一個等 待執行緒去處理包。當一個執行的執行緒完成處理,將再次呼叫GetQueuedCompletionStatus ,此時系統允許另一個等待執行緒去處理包。
  系 統也允許一個等待的執行緒處理包如果執行的執行緒進入任何形式的等待狀態,當這個執行緒從等待狀態進入執行狀態,可能會有一個很短的時期活動執行緒的數量會超過 NumberOfConcurrentThreads 的值,此時,系統會通過不允許任何新的活動執行緒快速的減少執行緒個數,直到活動執行緒少於NumberOfConcurrentThreads 的值。
參考連結:
CreateIoCompletionPort和完成埠

GetQueuedCompletionStatus

  完成埠GetQueuedCompletionStatus返回值的問題

  先看看GetQueuedCompletionStatus函式的完整宣告:

BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort,       
    LPDWORD lpNumberOfBytes,    
    PULONG_PTR lpCompletionKey, 
    LPOVERLAPPED *lpOverlapped, 
    DWORD dwMilliseconds
);

  再看看MSDN上對其返回值的說明:

  If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped parameters.
  如果函式從完成埠取出一個成功I/O操作的完成包,返回值為非0。函式在lpNumberOfBytes, lpCompletionKey, and lpOverlapped指向的引數中儲存相關資訊。

  If *lpOverlapped is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the lpNumberOfBytesTransferred and lpCompletionKey parameters. To get extended error information, call GetLastError. If the function did not dequeue a completion packet because the wait timed out, GetLastError returns WAIT_TIMEOUT.
  如果 *lpOverlapped為NULL並且函式沒有從完成埠取出完成包,返回值則為0。函式則不會在lpNumberOfBytes and lpCompletionKey所指向的引數中儲存資訊。呼叫GetLastError可以得到一個擴充套件錯誤資訊。如果函式由於等待超時而未能出列完成包,GetLastError返回WAIT_TIMEOUT.

  If *lpOverlapped is not NULL and the function dequeues a completion packet for a failed I/O operation from the completion port, the return value is zero. The function stores information in the variables pointed to by lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped. To get extended error information, call GetLastError
  如果 *lpOverlapped不為空並且函式從完成埠出列一個失敗I/O操作的完成包,返回值為0。函式在指向lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped的引數指標中儲存相關資訊。呼叫GetLastError可以得到擴充套件錯誤資訊。

  If a socket handle associated with a completion port is closed, GetQueuedCompletionStatus returns ERROR_SUCCESS, with lpNumberOfBytes equal zero.
  如果關聯到一個完成埠的一個socket控制程式碼被關閉了,則GetQueuedCompletionStatus返回ERROR_SUCCESS,並且lpNumberOfBytes等於0

參考連結
GetQueuedCompletionStatus的返回值

PostQueuedCompletionStatus

函式原型:

BOOL WINAPI PostQueuedCompletionStatus(
  _In_     HANDLE       CompletionPort,
  _In_     DWORD        dwNumberOfBytesTransferred,
  _In_     ULONG_PTR    dwCompletionKey,
  _In_opt_ LPOVERLAPPED lpOverlapped
);

CompletionPort:指定想向其傳送一個完成資料包的完成埠物件。
dwNumberOfBytesTrlansferred:指定—個值,直接傳遞給GetQueuedCompletionStatus函式中對應的引數
dwCompletlonKey:指定—個值,直接傳遞給GetQueuedCompletionStatus函式中對應的引數
lpoverlapped:指定—個值,直接傳遞給GetQueuedCompletionStatus函式中對應的引數

  可以看到上面後三個引數都可以傳遞給GetQueuedCompletionStatus,這樣—來。—個工作者執行緒收到傳遞過來的三個GetQueuedCompletionStatus函式引數後,便可根據由這三個引數的某一個設定的特殊值,決定何時應該退出。例如,可用dwCompletionPort引數傳遞0值,而—個工作者執行緒會將其解釋成中止指令。一旦所有工作者執行緒都已關閉,便可使用CloseHandle函式,關閉完成埠。最終安全退出程式。
  PostQueuedCompletionStatus函式提供了一種方式來與執行緒池中的所有執行緒進行通訊。如,當使用者終止服務應用程式時,我們想要所有執行緒都完全利索地退出。但是如果各執行緒還在等待完成埠而又沒有已完成的I/O 請求,那麼它們將無法被喚醒。
  通過為執行緒池中的每個執行緒都呼叫一次PostQueuedCompletionStatus,我們可以將它們都喚醒。每個執行緒會對GetQueuedCompletionStatus的返回值進行檢查,如果發現應用程式正在終止,那麼它們就可以進行清理工作並正常地退出。
  這個函式成功返回0,失敗返回非0,但是如果當此函式返回 false,並且當 GetLastError() == ERROR_IO_PENDING 的時候,認為它是成功的。

參考
MSDN
Can PostQueuedCompletionStatus ever fail?

WSASocket

  函式原型

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

  引數說明:
af:[in]: 一個地址族規範。目前僅支援AF_INET格式,亦即ARPA Internet地址格式。
type:新套介面的型別描述。
protocol:套介面使用的特定協議,如果呼叫者不願指定協議則定為0。
lpProtocolInfo:一個指向PROTOCOL_INFO結構的指標,該結構定義所建立套介面的特性。如果本引數非零,則前三個引數(af, type, protocol)被忽略。
g:保留給未來使用的套接字組。套介面組的識別符號。
iFlags:套介面屬性描述。

Socket與WSASocket的區別

  socket和UNIX相容,等價於用預設選項呼叫WSASocket。
  WSASocket可以使用WinSock特有功能,比如重疊IO,用dwflags指定。

  wsa的a是指api,用於區別spi,因為在spi中還有wspsocket,wspaccept等…再說一下winsock分兩部分:winsock api,winsock spi。。。

  WSASocket()在Socket()上擴充套件了一些功能,可以讓你詳細的來“定製”,一個socket如果只是簡單的使用就沒有必要使用WSASocket()

WSASocket用於非阻塞,很好用,Socket用於阻塞,也可採用多執行緒實現非阻塞

函式返回的錯誤程式碼:

WSANOTINITIALISED                在呼叫本API之前應成功呼叫WSAStartup()。
WSAENETDOWN                 網路子系統失效。
WSAEAFNOSUPPORT                不支援指定的地址族。
WSAEINPROGRESS                一個阻塞的WinSock呼叫正在進行中,或者服務提供者仍在處理一個回撥函式
WSAEMFILE                                無可用的套介面描述字。
WSAENOBUFS                        無可用的緩衝區空間。套介面無法建立。
WSAEPROTONOSUPPORT        不支援指定的協議。
WSAEPROTOTYPE                指定的協議對於本套介面型別錯誤。
WSAESOCKTNOSUPPORT        本地址族不支援指定的套介面型別。
WSAEINVAL                                g引數非法。

其實這個函式每個引數都有很複雜的講解。
具體可以參見MSDN文件

WSAEventSelect

  WSAEventSelect模型是WindowsSockets提供的一個有用非同步I/O模型。該模型允許在一個或者多個套接字上接收以事件為基礎的網路事件通知。Windows Sockets應用程式在建立套接字後,呼叫WSAEventSelect()函式,將一個事件物件與網路事件集合關聯在一起。當網路事件發生時,應用程式以事件的形式接收網路事件通知。
  使用這個模型的基本思路是為感興趣的一組網路事件建立一個事件物件,再呼叫WSAEventSelect()函式將網路事件和事件物件關聯起來。當網路事件發生時,Winsock使相應的事件物件受信,在事件物件上的等待函式就會返回。
  Winsock中建立事件物件的函式是WSACreateEvent,定義如下:

WSAEVENT WSACreateEvent(void); //返回一個手工重置的事件物件控制程式碼

  建立事件物件之後,必須呼叫WSAEventSelect函式將指定的一組網路事件與它關聯在一起。

  WSAEventSelect模型簡單易用,也不需要視窗環境。該模型唯一的缺點是有最多等待64個事件物件的限制,當套接字連線數量增加時,就必須建立多個執行緒來處理I/O,也就是所謂的執行緒池。
  與WSAAsyncSelect一樣也是一種非同步事件通知模型,不同的是WSAAsyncSelect是與視窗控制程式碼關聯在一起的,必須要要視窗才行,而WSAEventSelect是與事件物件關聯的。這個模型的基本思路是為感興趣的一組網路事件建立一個事件物件,再呼叫WSAEventSelect函式將網路事件和事件物件關聯起來。當網路事件發生時,winsock使響應的事件物件受信,在事件物件上等待的函式就會立即返回。之後呼叫WSAEnumNetworkEvents函式便可獲得到底發生了什麼網路事件(FD_READ/FD_ACCEPT/FD_CLOSE等等)。

WSARecv

函式原型:

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
);

  將一個套結字邦定到完成埠後,呼叫WSARecv和WSASend會立即返回,提高了系統的效率。可以以完成埠為引數呼叫 GetQueuedCompletionStatus 通過返回引數 lpOverlapped結構來區分來WSARecv和WSASend 。這樣主程式就可以完全等待接受新的連線,執行緒程式來等待WSARecv和WSASend是否完成了。

相關文章