天天看點

Windows完成端口 IOCP模型(一)

1 Windows完成端口基本介紹

2他是隻能在Windows下的基于SOCKET事件管理的模型

3與select不同,select需要多次重置管理句柄,IOCP隻要一次

4有事件後select需要操作擷取資料,而IOCP通知你的時候說明資料操作好了

5select管理句柄的數目有限,IOCP沒有限制

6IOCP支援多線程同時等待。

我的設計思路一個線程用來偵聽accept事件, 一個線程來偵聽SOCKET的IO事件,

大部分架構都是這樣, 其實可以隻使用一個線程做異步SOCKET就完全足夠了,現在

使用多線程來 就是看看這個IOCP的多線程用法,

在這之前先要了解下 異步通信 和 重疊I/O模型

重疊I/O模型: http://blog.51cto.com/12158490/2058180

我建議使用單線程 原因: 應為多個線程,拿到資料後,還是要發送到另外一個線程裡面去,

然後做成事件隊列, 也就是你線程拿到資料後, 還是要按隊列線程,進行邏輯處理,多線程沒意義。

2完成端口内部運作流程

完成端口的做法:事先開好幾個線程,你有幾個CPU我就開幾個,首先是避免了線程的上下文切換,因為線程想要

執行的時候,總有CPU資源可用,然後讓幾個線程等着,等到有使用者請求到來的時候,就把這些請求都加入到一個

公共消息隊列中, 然後這幾個開好的線程就排隊逐一去從消息隊列中取出消息并加以處理, 這種方式實作了

異步通信和負載均衡的問題,因為他提供了一種機制來使用幾個線程"公平的"處理來自多個用戶端的輸入/輸出

,并且線程如果沒事幹的時候,也會被系統挂起,不會占用CPU。

3WSAAsyncSelect或者WSAEventSelect和完成端口

WSAAsyncSelect或者WSAEventSelect兩種異步模型, 這兩種模式一定沒有使用overlapped(重疊)機制,

就不能算是真正的異步,可能是其内部維護了一個消息隊列,這兩個模式雖然實作了異步接收,卻不能異步的發送,

完成端口他是先把使用者資料接收回來後在通知使用者直接來取,而這兩種模式隻會接到資料到達的通知,隻能是由

應用程式自己去recv資料,性能上就發送了差異

要實作異步通信, 就要一個很強的I/O資料結構,叫重接結構"Overtlapped",

Window所有異步通信都是基于他,完成端口也不例外

就是執行I/O請求的時間與線程執行其他任務的事件是重疊(overlapped)的,

重疊結構是異步通信機制實作的一個核心資料結構,因為幾乎所有的網絡操作例如發送/接收,都要用

WSASend()和WSARecv()代替,參數裡面都會要附帶一個重疊結構,因為重疊機構可以了解為是一個

網絡操作的ID号,也就是說我們要利用重疊I/O提供的異步機制的話,每一個網絡操作都要有一個唯一的ID号,

因為進來系統核心,一看到有重疊的I/O的調用進來了,就會使用其異步機制,并且作業系統就隻能靠

這個重疊結構帶有的ID号來區分是哪一個網絡操作了,然後核心裡處理完畢, 根據這個ID号,把對應的資料傳上去.

4完成端口基本的使用流程 

完成端口也分步驟的

1調用CreateIoCompletionPort()函數建立一個完成端口,而且在一般情況下,我們需要并且隻需要

建立一個完成端口,把他的句柄儲存好, 之後隻要使用這一個句柄就行了。

2根據和客戶的I/O操作最好是自己建一個工作線程

3接入Socket連接配接, 有兩種方式,1是和别的模型一樣,有一個獨立的線程,專門來accept用戶端的連接配接,

二是用性能更好的異步AcceptEx()請求

4每當有用戶端進來的時候,還是調用CreateIoCompletionPort()函數,這裡不是建立完成端口,

而是把新連入的Socket句柄, 和你建立的完成端口綁定在一起。

5用戶端連入後,我們要向這個用戶端Socket送出一個請求,如接收檔案要調用WSARecv()然後系統

就會去執行接收資料的操作, 就不用我們管了。

6 然後就要調用GetQueuedCompletionStatus()(是一個阻塞函數)裡面是掃描端口的隊列裡是否有

網絡通信的請求存在(例如讀取資料,發送資料等),一旦有,就會将這個請求從完成端口的隊列取回來,

繼續執行本線程的後續代碼,處理完畢後, 必須要再次 投遞網絡通信請求(WSARecv),如此循環。

5AcceptEx和Accept的差別

AcceptEx和Accept最大的差別,就是取消了阻塞方式的accept調用,也就是說AccentEx也是通過

完成端口來異步完成的。

這樣做的好處就是:如果短時間内用戶端并發連接配接請求不是很多,accept和AcceptEx在性能上差別不大,

雖然我們建立Socket隻用一行SOCKET s =socket(...)一行代碼,但是系統内部建立一個Socket是相當

耗費資源的,因為Winsock2是分層的機構體系,建立一個Socket需要用到多個Provider之間進行處理,

最終形成一個可用的套接字,總之,建立一個Socket的開心是相當高的。

AcceptEx比Accept強三點:

(1)最關鍵的是AcceptEx在用戶端連入之前,就把用戶端的Socket建立好了,也就是說,AcceptEx是先建立

Socket,然後發出的AcceptEx調用,也就是說,在進行用戶端的通信之前,無論是否有用戶端連入,

Socket都是提前建立好的,而不需要想accept是在用戶端連入之後,在現場話費時間建立Socket,

(2)相比Accept隻能阻塞方式建立一個連入的入口,對于大量的并發用戶端來講,是在是有點擠,

而AcceptEx可以在完成端口上投遞多個請求,還有用戶端連入的時候,就非常優雅而且從容.

(3)AcceptEx還有一個優點,就是投遞AccepEx的時候,收取用戶端發來的第一組資料,這是同時

進行的, 也就意味着,如果用戶端隻是連入但不發送資料的話,我們就不會收到這個AccepeEx完成的通知,

異步的AcceptEx使用起來比accept要麻煩。

由于時間關系,在下篇部落格 會詳細介紹完成端口的用法

實作圖檔中的功能

 本文轉自超級極客51CTO部落格,原文連結:http://blog.51cto.com/12158490/2058203,如需轉載請自行聯系原作者

繼續閱讀