IOCP 系列函數講解

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是否完成了。

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