為了在IOCP中使SOCKET句柄復用,我使用了DisconnectExt函式,一般情況下客戶端與服務端建立連接后,服務端會投遞一個WSARecv例程給IOCP以接收來自客戶端的資料,而服務端主動斷開連接時會投DisconnectEx例程。
問題描述:
當服務端主動提交DisconnectEx例程后正常情況是先收到WSARecv完成事件 然后收到DisconnectEx完成事件并且在DisconnectEx事件中做一些資源清理作業然后把此句柄提交給AcceptEx佇列中。但問題是有時候先接收到的是DisconnectEx完成事件然后再接收到是WSARecv完成事件,這種情況造成一些問題比如當清理作業完成后IOCP會把這個SOCKET分配給一個新的連接,這個時候可能收到的WSARecv完成事件是原來舊的連接上的。
我在DisconnectEx之前也用過了CancelEx來取消IO但無濟于世,問教大家有什么辦法能在接收到斷開事件前讓WSARecv完成事件先達到,這樣才能順序條理的釋放句柄上的資源。
uj5u.com熱心網友回復:
你不能在WSARecv投遞以后然后回應接收到資料,再這之后再投遞DisconnectEx嗎?uj5u.com熱心網友回復:
估計你沒有明白我的意思,一般情況下客戶端與服務端建立連接后,服務端總是會提交WSARecv例程來等待客戶端發資料過來,服務端也不知道哪一個包是客戶端發的最后一個包,所以正常情況下服務收到一個包之后會立即投遞下一個WSARecv例程,只有客戶端或服務端主動斷開時才能收到最后的WSARecv的完成事件。我所描述的問題是DisconnectEx后,WSARecv完成事件是在DiscconectEx完成事件之后到達的,這個不符合我的預期,我期望的是DisconnectEx后,WSARecv完成事件先到達DisconnectEx完成事件后達到。
希望各位能幫忙,謝謝!
uj5u.com熱心網友回復:
自己弄個變數記錄一下,收到不期望的WSARecv完成事件時忽略之?uj5u.com熱心網友回復:
zhao4zhong1-----------------------------------------------------
我目前確實是這樣做的,我的想法就是有沒有其它更好的方法來解決這個問題!希望大家暢所欲言!
uj5u.com熱心網友回復:
看來你我這種檔次的英雄所見略同啊!
不知道還有沒有更高端大氣上檔次的英雄供咱倆一同膜拜?
uj5u.com熱心網友回復:
在WSASend或WSARecv成功后,使用InterlockedIncrement計數在處理完一個IO請求后,使用InterlockedDecrement計數
當要斷開連接時,標記一個狀態Disconnecting=True,投遞一個IO通知斷開連接(PostDisconnectEx),同時計數+1
收到DisconnectEx的IO時,觸發OnDisconnect后,計數-1
所有可能觸發斷線的地方,判斷Disconnecting是否=True,若Disconnecting=True時,判斷計數是否=0
計數=0時,在一個統一的介面(ReleaseClient)回收相關資源
有程序觸發ReleaseClient時,表示所有IO回傳完成,可以安全回收背景關銑澩
uj5u.com熱心網友回復:
在WSARecv完成事件接收后,在其資料處理后呼叫DisconnectEx,不要無序呼叫uj5u.com熱心網友回復:
今天正好在重新設計這一套東西,雖然這是個墳,為了以后別人少走彎路,我說下我的處理方法。首先回答核心問題:不管你的套接字上投遞了多少個事件,一旦你投遞了DisconnectEx,那么所有事件都是并發回傳,除非你使用了單執行緒IOCP,否則他們沒有順序,只有并發。
樓上的觀點取決于你的服務端是不是應答式的,一問一答得機制,確實可以保證“每一個套接字上任何時間只存在一個投遞事件”,這也是C1M以上并發量的做法。
但如果要求網路IO必須是異步的,就不可能滿足“一個套接字上任何時間只存在一個事件” 這一潭訓本要求,甚至從這個問題延申出去的發送資料積壓,也會同時體現出來,當然這里先不說多余的問題。那么為什么我們還需要這種方式呢?答:請參考游戲服務端。
我使用了一個IO控制類(class ioctl)來管理三個 擴展的重疊結構(OVERLAPPEDEX)物件
一個AcceptEx和WSArecv,
一個DisconnectEX,
一個WSASend.
結構大致如下:
struct OVERLAPPEDEX{
OVERLAPPED ol;
ioctl *ctl;
char *buf;
unsigned long len;
int opt;//表示用來IO操作的模式:
//FD_READ=WSARecv事件
//FD_WRITE=WSASend事件
//FD_ACCEPT=AcceptEx事件
//FD_CLOSE=DisconnectEx事件
};
同時在class ioctl中,有一個int opts; 它可以是 FD_READ, FD_WRITE, FD_ACCEPT, FD_CLOSE的任意組合。
在投遞操作之前,OVERLAPPEDEX.opt=具體操作;時,ioctl.opts |= 具體操作;
在事件完成時,ioctl.opts ^= OVERLAPPEDEX.opt;
讀寫都加鎖。
這樣子處理之后,那么服務端只要滿足以下幾個條件,就可以正確的處理這種并發問題:
1、投遞AcceptEx時,必須保證ioctl.opts為0,也就是所有事件都已經回傳。
2、FD_ACCEPT是一個獨立的值,絕對不會包含其他操作共存,當ioctl.opts==FD_ACCEPT時,拒絕之后的所有操作。
3、FD_CLOSE可以與FD_READ,FD_WRITE共存,但是一個套接字在其有效期內,只允許投遞一次FD_CLOSE。
4、一個投遞過FD_CLOSE的套接字,拒絕之后的FD_READ與FD_WRITE操作。
accept:192,168,2,11:65405
2019/09/21 06:40:50 ioctl::read ok!
2019/09/21 06:40:50 IO完成:1, bytes:7
接收完畢!
收到客戶端資料:啦啦啦
2019/09/21 06:40:50 ioctl::_write ok!
2019/09/21 06:40:50 IO完成:2, bytes:13
發送完畢!
2019/09/21 06:40:50 ioctl::read ok!
2019/09/21 06:40:50 ioctl::disconnect ok!
2019/09/21 06:40:50 IO完成:1, bytes:0
2019/09/21 06:40:50 IO完成:32, bytes:0
2019/09/21 06:40:50 ioctl::accept ok!
看日志可以知道,ioctl::read與ioctl::disconnect在一起呼叫了,之后也是并發完成,到最后一個事件完成時,才去把套接字acceptex回去。
題外話1:異步網路IO會帶來發送資料積壓的問題,這個問題沒有什么好辦法來解決,只能限制最大積壓數,超過這個數量,要么斷開連接,要么就把資料拋棄掉。
題外話2:異步網路IO模型并不適合IOCP來處理,應該考慮去linux使用epoll,因為異步網路IO追求的不是并發量。
題外話3:C1M以上的并發量時,IOCP將全面超越epoll,因為應答式服務端上層可以不需要任何讀寫鎖處理,資料可以不需要任何多余的拷貝。
uj5u.com熱心網友回復:
沒這么復雜吧,我記得DisconnectEx引發的WSARecv收不到任何東西或者有其他判斷條件可以判斷。WSARecv完成例程里要先判斷這些條件的,不符合直接rerurn就行了吧。uj5u.com熱心網友回復:
我是回識訓來再使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/35254.html
標籤:網絡編程
上一篇:關于addr.sin_addr.S_un.S_addr的小問題
下一篇:作業系統
