開門見山,直接就事論事。
假如有這麼一個基于IOCP模型的Server,這個Server提供的所有服務中有這麼一種服務……檔案下載下傳,我們再假設Server端存有一個20G的檔案,用戶端這時發送一個請求到服務端來,用戶端要求下載下傳這個20G的檔案,由此可能引發一系列讓人頭疼的問題(不談TransmitFile,我們談WSASend)
先給出一段僞代碼,這段代碼肯定是有問題的,如下:
//在某個線程裡,我們開始向某個用戶端瘋狂發送這20G的檔案了
while(true)
{
char *data=new char[PER_SEND_DATA_LEN];
::ReadFile(...,data,PER_SEND_DATA_LEN,&len,...);
io_data.wsabuf.buf=data;
io_data.wsabuf.len=len;
::WSASend(...,&io_data.wsabuf,...);
delete []data;
if(len<PER_SEND_DATA_LEN) break;
}
這樣無節操的發送基本上會得到一個WSAENOBUFS(緩沖區不足)錯誤,這個不是什麼大問題,不會影響到IOCP底層子產品的設計,值得注意的是下面一個問題——資料發送不完全
接着上面的例子繼續思考下去。
假如其中一個WSASend投遞長度為PER_SEND_DATA_LEN的資料,而GetQueuedCompletionStatus(...,<code>&BytesTransferred,...)傳回結果卻表明這次發送沒有進行完全,BytesTransferred<PER_SEND_DATA_LEN,而後續的WSASend函數正源源不斷地投遞新的請求,即使我們在Get函數傳回後,再次投遞WSASend請求補發剩餘資料,也無法保證這樣的補發可以趕在其他WSASend的前面。</code>
<code></code>
<code>如果以上推測成立的話,那麼資料将不可避免的出現亂序。</code>
當然,這隻是一種擔心,實際上到目前為止,本人還沒有遇到過這種情況,個人認為:這種情況應該不會發生,一定是哪些地方還沒有搞透徹,否則重疊的意義何在?不過,網上擔心這種情況的人也不少,以下是提出這些問題的連結(不保證都是原創):
<a href="http://blog.csdn.net/skiing_886/article/details/8044186">http://blog.csdn.net/skiing_886/article/details/8044186</a>
<a href="http://bbs.eyuyan.com/read.php?tid=306324">http://bbs.eyuyan.com/read.php?tid=306324</a>
上面兩個連結中,其中一個是篇部落格,部落格中明言WSASend帶重疊結構投遞請求時,不會出現資料發送不完全的情況,本人比較容易相信别人,于是我就信了。就算那篇部落格說錯了,本人也還是覺得資料亂序的情況不會發生。
下面,本人想就這個問題,結合IOCP的完成隊列說一些自己的猜想(完全是猜想,假設WSASend所投遞的資料有可能發送不完全)。
首先,IOCP維護着一個的隊列,這個隊列沒有好妖魔化的,就是一個先進先出的資料結構,效率很高就是了。問題是,是不是我們每次調用WSASend都将導緻一次入列,而每次GetQueuedCompletionStatus函數傳回都會導緻一次出列呢?
結合上面的那個下載下傳檔案的例子,細細一想,如果每次GetQueuedCompletionStatus傳回都要出列一次資料包的話,那一旦出現資料發送不完全的情況,即使我們再次調用WSASend補發,那也隻能将資料附加到完成隊列的頭部,亂序是必然的。
但是,換個思維,這種問題連我都能想到,微軟那麼多高智商人才難道都是吃幹飯的嗎?
是以,我認為,當資料發送不完全的時候,GetQueuedCompletionStatus應該是不會出列資料的,而可能是占着茅坑不拉屎,等待WSASend再次補發未完成的資料發送,而這個WSASend肯定不會導緻資料資料重新入列,而隻是給一個繼續發送的信号。
如果是我的,我肯定會這樣設計,否則,那就隻能采取保險的做法,WSASend不重疊投遞,一次投遞之後需要等待Get函數傳回再決定是要投遞一個補發的WSASend請求還是投遞下一個完整資料包的WSASend請求。這種做法雖然保險,但這突出了IOCP的巨大缺陷,正如我前面所說的,重疊的意義何在?WSASend還有必要帶着個OVERLLAPED結構嗎?
是以,我認為,這種情況的資料亂序不可能發生,不用擔心,嗯,不擔心……
說到最後,再順便提一下STL。STL的出列操作是Pop,它是不帶任何傳回值的出列,要想知道出列了什麼資料必須在Pop之前調用front方法。有人強烈鄙視這種設計,認為這種設計極其别扭。要知道,STL的設計者必然是高手,設計成這樣自然有理由,隊列這種東西,要想通用的話,最好還是要把‘看’和‘取’分開,STL的設計是綜合考慮的結果。