以下是windows平台下兩個函式的宣告:
int wsasend(
__in socket s,
__in lpwsabuf lpbuffers,
__in dword dwbuffercount,
__out lpdword lpnumberofbytessent,
__in dword dwflags,
);int wsarecv(
__in socket s,
__in_out lpwsabuf lpbuffers,
__in dword dwbuffercount,
__out lpdword lpnumberofbytesrecvd,
__in_out lpdword lpflags,
其中lpbuffers,dwbuffercount兩個引數是可以同時為空和0 的.
預設情況下,作業系統為每乙個套接字分配兩個緩衝區分別用於緩衝傳送資料和接受資料,所謂緩衝就是:
這兩個緩衝區由作業系統管理,並且屬於核心位址空間,是非分頁的(non-paged pool ).
傳統模式下,我們直接呼叫這個兩個api進行重疊io操作,並傳遞我們的應用層緩衝區位址,這個時候作業系統典型的處理方法如下:
無論是發還是收,一旦應用層記憶體被鎖住,這塊記憶體就不能從物理記憶體分頁出去.作業系統會限制這些被鎖住的記憶體的數量,一旦達到這個限制,就會返回wsaenobufs錯誤.如果應用層在每乙個連線上發起大量重疊io請求,隨著連線數的增長,很可能就達到這個限制的值.一方面是因為重疊io運算元量上的增長,另一方面是因為當前系統的分頁單位是固定的,即使應用層只有乙個位元組的操作請求,作業系統仍然需要付出一頁(一般是4k)的代價.
如果伺服器希望能處理非常多併發連線,可以在每個連線的讀請求時投遞乙個0位元組的讀操作,即在wsarecv的時候為lpbuffers和 dwbuffercount分別傳遞null和0引數.這樣做就不會存在記憶體鎖定帶來的資源緊張問題,因為沒有記憶體需要被鎖定,一旦有資料被收到,操作系 統就會投遞完成通知.這個時候服務端就可以去套接字接受緩衝區取資料了,有兩種方法可以得知到底有多少資料可以讀,一種是通過ioctlsocket結合 fionread引數去"查詢",另一種就是一直讀,直到得到wsaewouldblock錯 誤,就表示沒有資料可讀了.另一方面在傳送資料的時候,仍然可以採用這種方案,原因在於對端的應用可能效率非常低下,或者陷入了某個死迴圈,導致對方的網 絡io層遲遲不呼叫recv/wsarecv,受tcp協議本身的限制,服務端需要傳送的資料就會一直pending,進而導致記憶體被核心鎖住.採用0字 節傳送方式後,應用層先投遞乙個空的wsasend,表示希望傳送資料,作業系統一旦判斷這個連線可以寫了,會投遞乙個完成通知,此時便可以放心投遞數 據,並且傳送緩衝區的大小是可知的,不會存在記憶體鎖定的問題.
這種方案適合最大化併發量,但也存在短處,首先就是資料傳送和接受的時候有乙個資料拷貝的代價,從網路上收到的資料 並不是直接放到應用層提交的緩衝區裡.另外乙個代價就是每一次讀和寫要經過乙個先請求後實施的操作,而傳統的方案是要一步到位.但正是這些差異避免了對系 統資源嚴重占用.
提到windows平台上的高效能io操作,就不得不提iocp(完成埠),上面的方案是完全適合icop模型 的.順帶提一下在這種模型下對同乙個套接字投遞多個讀和寫操作的情況,iocp可以保證多個同乙個控制代碼上的多個重疊操作在資料處理上是有序的,也就是說先 提交的重疊操作先處理,但是不保證你收到的完成通知是有序的.
IOCP 零位元組緩衝解鎖疑問
在開codeproject網上看乙個iocp的例子 在解決wsaenobufs問題的方法中,使用了投遞乙個使用零緩衝區的wsarecv來達到解鎖 作者網頁翻譯 wsaenobufs問題這個問題通常很難靠直覺發現,因為當你第一次看見的時候你或許認為是乙個記憶體洩露錯誤。假定已經開發完成了你的完成埠伺服...
位元,位元組,千位元組
如果你被人問到什麼是位元?什麼是位元組?這些網路中資料傳輸的單位一定會讓你有點毫無頭緒的感覺。沒關係,這些都很好理解,看過了下面的內容你就可以有條有理的把問題一一解答了。首先說說最小的單位,我們通常都叫做bit,也就是位元,有的時候也稱為位。但不管怎麼稱呼,他們都是二進位制數中最小的單位。單位的概念...
位元組對齊 8位元組對齊
參考博文 參考1 參考2 參考3 在記憶體管理中經常使用位元組對齊來管理分配的記憶體。1 原理 2 演算法 2.1unsigned intcalc align unsigned int n,unsigned align 2.2 更好的演算法 unsigned intcalc align unsign...