網絡編程與IOCP

Windows下的網絡編程,IOCP(IO Completion Port)作爲windows下效率最好的網絡編程模型,可能是必須瞭解和掌握的知識。要理解IOCP,也必須瞭解有關網絡編程的方方面面的知識。

有很長時間沒有做網絡編程方面的工作,因爲需要,必須重新撿起來這方面的知識,同時整理成章。

作爲隨筆,相對比較雜亂,更多注重理解領會,文字多些,而代碼少些。

一,相關知識:阻塞與非阻塞(blocking-Non-blocking),同步與異步(synch-asynch),重疊(overlapped)
網絡編程涉及到許多方面的知識,而進程調度,IO管理,TCP/IP模型,多線程編程,這些只是作爲網絡編程的基礎知識,在這裏不再談及。
作爲網絡編程的最重要的幾個概念,在這裏重新梳理一下。

※ 同步和異步
同步和異步的概念更多見於通信領域, 例如在串行通信中有定義:
同步通信是一種連續串行傳送數據的通信方式,一次通信只傳送一幀信息,幀信息包括n位。異步通信在發送字符時,所發送的字符之間的時間間隔可以是任意的,但需要在每一個字符的開始和結束的地方加上開始位和停止位。在這裏,同步通信要求接收端時鐘頻率和發送端時鐘頻率一致,簡單來說爲的是不發生數據的丟失和錯位。異步則因爲有開始和停止位,則在接收端可以提前做好準備了,顯然同步通信的效率要高。至此,我們可以簡單的理解同步爲 時鐘頻率一致,雙方步調一致的工作。回過頭來看在網絡編程中的同步與異步,與通信的中概念有所不同。同步是指當線程開始IO操作後即進入等待,直到IO完成再進行下一步的工作。異步則是線程發出IO請求後將IO工作交給內核,不進行等待轉而處理其他工作,直到系統內核通知線程IO完成。其根本在於是否等待。與串行通信同步的異同之處可以揣摩一二。對於IO來說,同步的效率要高,而對於整個系統來說,顯然異步的系統利用率會更好。下圖很形象的描述了同步和異步的過程。BTW,所謂重疊IO,通常就是指的異步IO。

網絡編程與IOCP
本圖來自於:
https://docs.microsoft.com/zh-cn/windows/desktop/FileIO/synchronous-and-asynchronous-i-o

(WaitForSingleObject -> NtWaitForSingleObject -> KeWaitForSingleObject,在這裏有些源碼,沒有資料不大理解,只能略窺原理。https://www.xuebuyuan.com/856418.html

※ 阻塞和非阻塞
很長一段時間,我一直沒搞懂同步和阻塞究竟區別在哪裏。
來看下對書上阻塞/非阻塞概念的理解(這裏參考Unix網絡編程卷1-6.2章I/O模型)。
書中將輸入操作分爲兩個步驟:1,等待數據準備好,2,從內核將數據拷貝到進程。以UDP爲例,recvfrom視爲系統調用,不論recvfrom是如何實現的,通常在調用這個函數時都由應用程序進入到系統內核,待內核將數據準備好,然後在稍後某個時間點返回到應用程序。這時我們稱我們的進程從recvfrom開始調用直到返回的整個時間段阻塞了。當recvfrom成功返回,我們的應用程序就可以處理數據報了(接收到數據)。(此即同步阻塞模型)
當我們將一個socket設置爲非阻塞時,等於在告訴內核,我請求的IO操作不能完成時,不要讓進程sleep,而是返回一個error。如果想要知道內核何時將數據準備好,只有通過不斷調用recvfrom來詢問內核,直到某一次recvfrom返回成功。(此即同步非阻塞模型)
將以上的概念與同步異步放在一起來理解,粗一看,都講的是IO請求之後,等待與否。細細分辨,差別有二。
1,其實同步異步的概念更要大些,廣泛些,例如線程同步,讀寫同步之類。就IO模型來說,以上面講到的輸入操作爲例,同步異步最大的區別在於,同步是將請求交給內核,內核將數據準備好,即告訴進程,由進程來處理數據IO。而異步則將請求交給內核後,全部工作由內核完成。數據IO結束後才通知進程。
2,我們可以認爲,阻塞僅僅是指調用recv,send等IO函數,函數是否立即返回的行爲。不論這些函數是否立即返回,其原則上都是同步模型,區別在於等待內核返回和循環去詢問內核是否OK。而異步則完全不需要去關心內核的狀態,只需要安心坐等內核完成後的通知就可以了(當然這個等待往往也是一個阻塞的線程來完成這個任務,只不過IO線程並不阻塞)。
由阻塞和同步排列組合出來四種模型,同步阻塞(常用),同步非阻塞(多次查詢效率低),異步阻塞(少見,我還難以理解這種實現),異步非阻塞(效率最高,IOCP即爲此種)

順便在提下 select模型(IO複用)
通過以上的介紹,我們應該很容易理解select模型了。select模型應該是一種同步阻塞模型,只不過阻塞的位置在select函數,而不是在IO函數。因爲select是可以同時等待多個socket的狀態,所以較簡單的阻塞模型效率要高。事實上同時IO的socket不是太多的話,使用多線程,每個線程使用阻塞模型來等待一個socket的狀態,可以達到select模型類似的效果。

Reactor與Proactor模式

參考文章:https://www.cnblogs.com/talenth/p/7068392.html
Unix網絡編程卷1(by W.Richard Stevens)

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