這個問題是這樣,如果在接收端(WSARecv)投遞了一個200字節的WSABUF,那麼你的工作器線程處理完成通知的時候,首先收到第一個200字節,此時你可以繼續投遞接收下來的200個字節(注意了,是得到上一個200字節通知并保存處理了才投遞下一個)這樣才能保證你的接收和處理1000個字節不亂。如果你一開始就投遞(而不是投遞一個之後,在得到通知處理之後依次投遞)了5個WSARecv,對應5個單IO操作數據,會有5個通知在隊列,當然在隊列裏這5個完成包順序是正確的。問題在于,你有多個工作器線程來分別處理這些通知,那麼雖然隊列裏是正確的,但你的工作器線程在處理通知和保存數據的時候,保存這些數據有可能出現亂序。根源在于投遞WSARecv的方式。解決方案雖然有,就是對發送的包加上編號header,在工作器線程中保存200字節的時候根據編號來處理.
1。IOCP一般只用于“響應socket的recv消息”,發送數據時,其線程載體未必是IOCP線程,建議使用其它線程池的線程(IOCP本質上就是1個線程池,速度稍微快一點而已)來處理業務邏輯;
2。IOCP照樣可用于“長連接socket”,無論是“大數量的并發訪問”還是“長時間的事務處理”,只要在收到數據之后,把數據投遞到另外1個線程池里處理,而IOCP就立即返回了,不會造成任何socket層面的延遲和阻塞(即:轉換處理事務的線程載體)。本人在win2k下測試過最高4000個并發連接(P2-350,28M);
3。我在項目里寫的服務器,用3個線程池:第1線程池是IOCP,處理recv消息;第2線程池是普通線程池,處理程序事務;第3線程池是普通線程池,用來send網絡數據;
--------------
我一般把你第三個線程池和第一個重合起來,這樣效率會更高。
我來個總結:
1,我會創建cpu*4的線程數處理,res,and send.
2.我會用cpu*8的線程數處理所有事務。
同時post cpu*4個accept,這樣同時最大的并行accept就會是cpu*4;
對于每個accpte的clinet post 1 read
接收的情況:
當clienta收到一個完成通知的時候,處理buff,把結果(list)放到client結點中,post event(注意給clienta ->addref())給2,2派出一個idle thread to 處理clienta ,clienta->release().
N個client的時候,情況類是。
發送情況。
在2中業務和暴風雨一樣復雜,而對于每個結點來說,都是single thread的,如果2沒有idle的線程,對于和結點clienta一樣的其他結點來說,他們的包結果來不級處理的時候,會list到自己的結點上。直到2有idle的線程來處理。但1中的線程還在快樂的接收的自己數據。
當2中某個thread的接受到一個命的時候,比如要發送一個數據,2 post event to 1,1從clienta中的list取出一個待發包,post write.這樣可以避免多個線程對同一個socket post wirte,大家都知道如果一個socket 在pending狀態時,不能對同一個overlay發起兩個post write.我們之所以用一個Overlay而不用多個,是為了程序著想,不需要太復雜。因為對于同一個包,有可能需要post 多次。這樣,我們可以通過結點的記錄上次發送了多少byte.從而正確的從上次的offsize post.
業務處理上:
我一般不會把分析包的fuction放在io處理線程中,也就是iocp中,我會暴路一個接口類,比如一個pure class.這樣做,可以很好的把類分離,并且可以應用到不同的應用中。
從上面的分析可以看出,真正的執行者肯定在2中,當2有一個包分析完成的時候,我會post一個event給2,也就是自己發事件給自己。讓2分配出另一個線程來處理業務,而原來的線程可以繼續分析包。2->2的情況,也不是一概而論,看具體的業務。但這樣做,就可以比較高效了。
我現在的處理方式上面,與樓上的有點不一樣.線程數量上面我不會固定,也就是不受CPU的多少而限制.但是會給出一個同步運行線程數參考值.比如CPU*2.但是實際運作過程當中會由于有多個網絡事件到來,而可能這個值會被耗光,也就是在某個時間里沒有線程處于GetQueuedCompletionStatue阻賽來處理下一個網絡事件,此時我就會考慮再添加一個例外線程,來做等待.由于多出了一個例外線程,所以可能已經超過允許同時運行的線程數量參考值,則哪一個線程完成任務后先做值檢測,若超出則看是處于阻塞狀態的線程數是否達到兩個,若是則自行結束(這時候會帶一個麻煩事情就是,該線程的退出會引發由其發出的I/O請求被取消,也會有一個網絡事件).
不過我發送時是直接使用WSASend拉交,也就是說可能會有兩個線程同時工作于一個連接之上.只是當有執行WSASend時,Sending計數加1,而下一輪需要發送時,先檢測Sending計數.如果有Sending,則直接把數據包附加到Sending隊列,如此也可以保證數據的順序性,同時不需要PostQueuedCompletionStatus,少一個切換過程,并且實現真正意義上的重疊IO,對于處理發送完成事件的線程,則可以一直發送WSASend到沒有待發數據為止,每一次完成通知則扣減一個完成計數,每提交一次WSASend則增加一個計數.
由于有多個線程引用一個連接的上下文信息,如果在刪除時直接刪除,則會報錯,所以在需要設置刪除前,先置刪除標識,所有線程遇到該標識將不再發送WSASend/WSARecv,另由一個檢測線程來對沒有(發送和接收)計數的數據進行清理(記得closesocket先,以阻止所有后來的網絡事件,在下一次檢測過程當中清理為其分配的資源,并刪除).
檢測線程主要有兩項工作,一是清理垃圾,二是針對不良連接,也就是在一段時間內沒有任何I/O操作的連接進行清理,也就是說如果是自己的客戶端則最好是在沒有數據交互的情況下發送時脈信息,對于部分需要驗證的服務,也可以在此對超時未進行身份驗證的連接清理掉.
從我個人做過的項目中講,一個連接就應該同時只會有1個r + 1個s 被投遞,否則出現工作線程恰逢時間片切換(多核CPU上尤其小心),網絡粘包半包時,邏輯協議包的解析上極為困難。
所謂廣播消息,往往只是部分廣播(例如區域同步),這個由邏輯層決定需要對哪些session進行消息發送就行了,socket層要做的只是給指定socket發送消息。
無論r/s,都應該有自己的緩沖隊列,r的用于處理粘包半包,s的用于流量緩沖控制。
此外,在我自己的應用中,網絡模塊(獨立進程)收到完整數據(能成功解析出邏輯包),再使用namedpipe(也用IOCP)轉發給具體工作進程,反向流程依然