零字节WSASend,WSARecv
其中lpBuffers,dwBufferCount兩個參數是可以同時為空和0 的.
默認情況下,操作系統(tǒng)為每一個套接字分配兩個緩沖區(qū)分別用于緩沖發(fā)送數據和接受數據,所謂緩沖就是:
- 應用層的發(fā)送數據先拷貝到發(fā)送緩沖區(qū),然后再由操作系統(tǒng)發(fā)送出去.
- 遠端發(fā)過來的數據先放在接受緩沖區(qū),等待應用層調用讀操作,把這些數據取走.
這兩個緩沖區(qū)由操作系統(tǒng)管理,并且屬于內核地址空間,是非分頁的(Non-paged pool ).
?
傳統(tǒng)模式下,我們直接調用這個兩個API進行重疊IO操作,并傳遞我們的應用層緩沖區(qū)地址,這個時候操作系統(tǒng)典型的處理方法如下:
- 發(fā)送:應用層調用WSASend,這個時候如果套接字發(fā)送緩沖區(qū)由足夠的空間,操作系統(tǒng)將把應用層提交的數據拷貝過來,這個操作立即完成并返回成功.如果套接字的緩沖區(qū)滿了,應用層提交的那塊內存就會被操作系統(tǒng)鎖住,并且返回一個WSA_IO_PENDING的錯誤,在發(fā)送緩沖區(qū)的數據處理完畢后,操作系統(tǒng)將直接將發(fā)送應用層緩沖區(qū)的數據,不再拷貝到緩沖區(qū),這個時候應用層將會收到完成通知.如果緩沖區(qū)仍然有空間,但是也不夠存放應用層請求的數據,仍然按照前一種情況處理.
- 接收:應用層調用WSARecv,很有已經有數據在套接字接收緩沖區(qū)中了,這個時候操作系統(tǒng)會直接將數據拷貝過來,這個調用將返回成功,同時系統(tǒng)投遞一個完成通知,應用在處理這個通知的時候將會知道本次操作的數據量.另一種情況是接受緩沖區(qū)沒有數據,應用層提交的緩沖區(qū)就會被鎖住,并且得到WSA_IO_PENDING錯誤,一旦這個連接收到了數據,操作系統(tǒng)會直接把數據拷貝用戶緩沖區(qū),并發(fā)出完成通知.
無論是發(fā)還是收,一旦應用層內存被鎖住,這塊內存就不能從物理內存分頁出去.操作系統(tǒng)會限制這些被鎖住的內存的數量,一旦達到這個限制,就會返回WSAENOBUFS錯誤.如果應用層在每一個連接上發(fā)起大量重疊IO請求,隨著連接數的增長,很可能就達到這個限制的值.一方面是因為重疊IO操作數量上的增長,另一方面是因為當前系統(tǒng)的分頁單位是固定的,即使應用層只有一個字節(jié)的操作請求,操作系統(tǒng)仍然需要付出一頁(一般是4K)的代價.
?如果服務器希望能處理非常多并發(fā)連接,可以在每個連接的讀請求時投遞一個0字節(jié)的讀操作,即在WSARecv的時候為lpBuffers和 dwBufferCount分別傳遞NULL和0參數.這樣做就不會存在內存鎖定帶來的資源緊張問題,因為沒有內存需要被鎖定,一旦有數據被收到,操作系 統(tǒng)就會投遞完成通知.這個時候服務端就可以去套接字接受緩沖區(qū)取數據了,有兩種方法可以得知到底有多少數據可以讀,一種是通過ioctlsocket結合 FIONREAD參數去"查詢",另一種就是一直讀,直到得到WSAEWOULDBLOCK錯 誤,就表示沒有數據可讀了.另一方面在發(fā)送數據的時候,仍然可以采用這種方案,原因在于對端的應用可能效率非常低下,或者陷入了某個死循環(huán),導致對方的網 絡IO層遲遲不調用recv/WSARecv,受TCP協(xié)議本身的限制,服務端需要發(fā)送的數據就會一直PENDING,進而導致內存被內核鎖住.采用0字 節(jié)發(fā)送方式后,應用層先投遞一個空的WSASend,表示希望發(fā)送數據,操作系統(tǒng)一旦判斷這個連接可以寫了,會投遞一個完成通知,此時便可以放心投遞數 據,并且發(fā)送緩沖區(qū)的大小是可知的,不會存在內存鎖定的問題.
這種方案適合最大化并發(fā)量,但也存在短處,首先就是數據發(fā)送和接受的時候有一個數據拷貝的代價,從網絡上收到的數據 并不是直接放到應用層提交的緩沖區(qū)里.另外一個代價就是每一次讀和寫要經過一個先請求后實施的操作,而傳統(tǒng)的方案是要一步到位.但正是這些差異避免了對系 統(tǒng)資源嚴重占用.
提到windows平臺上的高性能IO操作,就不得不提IOCP(完成端口),上面的方案是完全適合ICOP模型 的.順帶提一下在這種模型下對同一個套接字投遞多個讀和寫操作的情況,IOCP可以保證多個同一個句柄上的多個重疊操作在數據處理上是有序的,也就是說先 提交的重疊操作先處理,但是不保證你收到的完成通知是有序的.
總結
以上是生活随笔為你收集整理的零字节WSASend,WSARecv的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: getsockname与getpeern
- 下一篇: IOCP中在WSASend以及WSARe