本文是解讀ASIO之前的一碟小菜。。。
ascent 2010的網路模組代碼採用了IOCP,代碼量很小。
帶著以下問題溜了一遍:
1.IOCP的亂序問題
如果同時對一個socket投遞多個recv或者send請求,那麼應用程式層會將TCP辛辛苦苦保證的有序
給打亂。
解決辦法很簡單,串列投遞。
這對於伺服器程式來說是可行的,畢竟它管理的是成百上千個sockets,每個2請求,那IOCP要處理
的也很多了。
ascent也是這麼做的(從socket類只含有兩個overlap對象一眼看出)。
但不是強制:發生了重複投遞,它只是寫了一條錯誤記錄檔。
2. send操作能否避開鎖?(recv無須鎖是很好做的,不提)
事實上我覺得是可以的,至少epoll是絕對可以的,邊寫邊想IOCP是否也可以。
從線程的角度看,一條串連的發送緩衝區的消費者是網路線程,生產者是邏輯線程。
即發送資料一定是邏輯線程發起的(前提是心跳包也要一併移給邏輯線程處理);
那麼緩衝區的讀遊標是由網路線程修改,寫遊標是由邏輯線程修改。
要做到無鎖,一定要保證共用變數只會被一個線程修改,且為volatile類型;
邏輯線程投遞寫請求,將資料放入發送緩衝,修改寫遊標;
對於IOCP來說,網路工作者線程可能有多個(一般來說是CPU數目的兩倍),
多個網路線程阻塞在GetQueuedCompletionStatus函數,儘管執行send請求的成功回調
不是由某個固定網路線程執行,但在同一時刻send請求的回調一定是由某一個線程單獨處理,
並調整讀遊標。
所以感覺iocp方式下,socket的發送緩衝區也無須鎖。
(也許考慮還不是很全面,待修正)
asent中使用了鎖來保護髮送緩衝區,但我認為是它的緩衝區結構設計問題導致的(很複雜,做成了兩段緩衝),如果做成簡單的不可擴充的
足夠大的環形緩衝區,是無須鎖的。(帶來一個問題:由於發送的資料可能不是在連續緩衝,發送操作請調用類似writev的函數,
當然WINDOWS支援的很好,WSASend與WSABUF共同實現gather-write)
在邏輯線程發送資料時,如果檢測到並沒有send操作正在掛起中,那麼就直接WSASend
3.是否單獨啟動LISTEN線程
ascent確實單獨啟動了一個監聽線程。不過有點惡的是,如果啟動了兩個監聽socket,那就啟動兩個監聽線程,
太浪費了。
監聽線程阻塞ACCEPT,並將新串連與IOCP關聯,同時投遞第一個RECV請求。
另記:
為什麼非阻塞socket,send也會阻塞?
A端發資料,B端不調用recv;
於是一會兒A端的發送緩衝區滿了,竟然阻塞在send調用;而不是返回-1和EAGAIN?
必須send時候指定MSG_DONTWAIT。。儘管是非阻塞socket
但是man手冊說了,設定了O_NONBLOCK,就不用指定這個。
艸,誤導人