IO完成埠(IOCP)
1 人贊了文章
1 概述
IO完成埠是windows下性能最好的IO模型。
windows下的socket也是一種IO,所以完成埠也是最好的socket IO模型。
不幸的是,IO完成埠非常複雜,理解它門檻已經非常很高,而正確使用的門檻比理解它還要高很多。
之所以這麼難理解,很大原因也在於沒有好的資料。MSDN的文檔已經相當良心的完善,但是還是有許多的問題沒有解釋清楚,整個模型的機制模糊不清。而網上的文檔只能作為入門,裡面的內容離實際可用的代碼有十萬八千里那麼遠,比如很多的文檔連發送的功能都沒有講到。至於udp使用iocp,就更少有資料了。
2 簡化模型圖
這個最簡單的模型,用來表示IOCP涉及到的對象。
這裡有三個對象,主線程,socket工作線程,系統API模塊。
上面是用戶代碼層,下面是系統的IOCP API層。主線程和工作線程都處於用戶代碼層。
3 簡化流程圖
這個最簡單的流程圖,表示了主線程和Socket工作線程的主要流程。
簡單的描敘一下:
(1)主線程通常是消息循環線程。主線程調用系統IOCP API發起操作(如WSARecv開始收Socket數據),請求系統完成該操作。
(2)系統收到操作請求,進行處理(如開始收Socket數據),並在處理完成時(如收到了60K數據),發出事件,事件會喚醒工作線程。
(3)工作線程通常是普通線程。阻塞等待事件,處理事件,並發送消息通知主線程(如"收到60K的Socket數據消息")。
(4)主線程收到消息,處理消息(如"收到60K的Socket數據消息"),處理業務邏輯(比如在界面上顯示:你收到一條新的消息)。
4 模型要素
4.1 非同步操作
非同步操作是一個函數,該函數立即返回,作用是請求系統完成一些事情,系統經過一段時間完成了,就觸發一個系統事件,喚醒等待事件的線程。
非同步操作有:WSASend(發送數據),WSARecv(接受數據),PostQueuedCompletionStatus(發自定義事件)。
4.2 系統事件系統事件有:Socket事件(發送SOCKET數據完成事件,收到數據事件,SOCKET斷開連接事件),自定義事件:通過PostQueuedCompletionStatus發送的事件。
4.3 主線程
主線程通常是業務邏輯的所在,我們這裡假設主線程是消息循環線程。
主線程建立和銷毀工作線程,建立的過程很簡單,這裡略去,銷毀的時候,是通過發送一個事件給工作線程,工作線程被喚醒並處理事件,退出。
主線程可以調用系統IOCP函數(如WSARecv函數接受Socket數據),該函數立即返回,不阻塞主線程。
主線程會收到工作線程發過來的消息(例如收到數據消息)。
4.4 工作線程
工作線程通常不做複雜的業務邏輯,是一個普通線程。
工作線程阻塞等待系統事件(GetQueuedCompletionStatus),每當收到系統事件,就會被喚醒,然後簡單處理事件:例如把收到的Socket數據包裝成一個消息,發送給主線程。
5 事件和操作
5.1 事件和操作之間基本關係
(1)只有發出操作,才可能收到事件。不發出操作,不會收到事件。
(2)發出一個操作,收到一個事件。
5.2 WSARecv操作
有可能收到「SOCKET收到數據事件」,「SOCKET斷開連接事件」。
調用WSARecv函數
(1)如果系統之前已經接收了Socket數據:
函數返回值為0,函數的出參里已經有收到的Socket數據了,但是,阻塞等待的線程依然會收到事件,事件的參數裡面也帶有Socket數據。
(2)如果系統之前沒有接收到Socket數據:
函數返回值不為0,如果WSAGetLastError為WSA_IO_PENDING時,表示投遞該操作給系統成功,阻塞等待的線程會收到事件(該事件有可能表示真的收到了數據,也可能是收數據失敗)。
5.3 WSASend操作
有可能收到「SOCKET發送數據完成事件」,「SOCKET斷開連接事件」。
當收到「發送數據完成事件」,並不代表數據真的發送完成,可能實際上只發了一部分,對方只收到一部分。
6 實際模型
實際的伺服器模型更加複雜,這個圖還是簡化了的,沒有畫上accept部分。
圖中,網路模塊和邏輯模塊都屬於用戶層代碼。
系統API模塊有兩個socket IO線程,一般來說,系統API層的線程數為:CPU數目*2。
網路模塊有四個SOCKET工作線程。一般來說,用戶層的SOCKET工作線程數為:CPU數目*4。
推薦閱讀:
※我讀過的最好的epoll講解
※《計算機網路:自頂向下方法》第二章配套實驗
※不走心
※C# Socket編程基礎入門
※網路通信引擎007. 用Socket API 6步建立TCP服務端