?
?
鄧立波 深圳,2007-8
作者聯(lián)系方式
:
email:
????????
libodeng@gmail.com
msn:?
????????
libodeng@gmail.com
tel:???? ???????? 13510275799
版權(quán)/著作權(quán)所有 (C) 2007 鄧立波 保留所有權(quán)利
警告:未經(jīng)作者許可,任何人或組織不得轉(zhuǎn)載,公開發(fā)布,拷貝,傳播本文獻的全部或部分
??
及時監(jiān)測連接被動關(guān)閉
??????
除非有特別要求,否則你應(yīng)該總是對每個連接保持一個掛起的接收
pending io
(使用
WSARecv
投遞)。如果用戶主動關(guān)閉連接,你的
GetQueuedCompletionStatus
調(diào)用將返回成功,但接收到的數(shù)據(jù)長度為
0
,你能根據(jù)這點檢測連接是否已被對方關(guān)閉。如果連接被重置或者
io
被取消(如果你調(diào)用了
CancelIo
的話),
GetQueuedCompletionStatus
將返回失敗,注意這時還應(yīng)該判斷
GetQueuedCompletionStatus
調(diào)用返回的
lpOverlapped
值,如果該值不為
NULL
,說明
iocp
已經(jīng)檢測到一個連接已經(jīng)中斷。
?
安全的關(guān)閉連接
??????
很多人寫的服務(wù)器網(wǎng)絡(luò)庫有一個難以接受的缺陷(包括我曾就職公司的一些同事),當(dāng)服務(wù)器程序主動關(guān)閉連接時,剛發(fā)往客戶端的包有時出現(xiàn)丟失,這時他們推薦的方式往往是發(fā)送數(shù)據(jù)后等待幾秒再關(guān)閉連接。豪無疑問,這是一種笨拙的實現(xiàn)方式,他們遇到的問題根源是什么呢?
??????
在非
IOCP
模式網(wǎng)絡(luò)程序中,你只要簡單的調(diào)用
closesocket
函數(shù)就可以確保數(shù)據(jù)在操作系統(tǒng)釋放
socket
之前安全到達對方,但在
IOCP
模式下,如果調(diào)用
closesocket
時有未決的
pending IO
將導(dǎo)致
socket
被重置,所以有時會出現(xiàn)數(shù)據(jù)丟失。正統(tǒng)的解決方式是使用
shutdown
函數(shù)(指定
SD_SEND
標(biāo)志),注意這時可能有未完成的發(fā)送
pengding IO
,所以你應(yīng)該監(jiān)測是否該連接的所有是否已完成(也許你要用一個計數(shù)器來跟蹤這些
pending IO
),僅在所有
send pending IO
完成后調(diào)用
shutdown
。
當(dāng)你調(diào)用
shutdown
時,也許數(shù)據(jù)仍然停留在操作系統(tǒng)的緩沖,操作系統(tǒng)將在數(shù)據(jù)發(fā)送完后發(fā)出一個
FIN
包來啟動關(guān)閉進程,客戶端接收完數(shù)據(jù)后,將接受到一個
0
長度的包,以此判斷連接已關(guān)閉(你寫的客戶端肯定有檢測連接關(guān)閉,不是嗎?),然后調(diào)用
closesocket
,這時服務(wù)器的
GetQueuedCompletionStatus
將接收到一個數(shù)據(jù)長度為
0
的包,這時你就可以調(diào)用
closesocket
,并釋放相關(guān)連接資源。
在絕大部分情況下上述的過程連接能完美的關(guān)閉。如果你特別注重服務(wù)器的安全性和健壯性,可能你還需要做一個“連接關(guān)閉隊列”,對每個已調(diào)用
shutdown
的連接放到這個隊列,然后定時的對這個隊列掃描,如果一個連接
5
秒(你也可以自己調(diào)整)還不能關(guān)閉,那么就強制關(guān)閉它。
?
處理大并發(fā)短連接時如何避免
TIME_WAIT
狀態(tài)
??????
關(guān)于如何避免
TIME_WAIT
這個問題,一直沒看到有效的處理方式(至少我沒有),
我將在這里披露一種有效的方式。回到上一段,我們最后調(diào)用了
closesocket
關(guān)閉連接,這時仍然可能出現(xiàn)
TIME_WAIT
狀態(tài),但注意這時所有的數(shù)據(jù)都已經(jīng)傳輸完畢,因此你可以強制關(guān)閉
socket
避免服務(wù)器連接進入
TIME_WAIT
(這時只會發(fā)出連接重置
RESET
包)
//
立即關(guān)閉
(
避免出現(xiàn)
TIME_WAIT
狀態(tài)
)
??????
?????? LINGER linger = {1,0};
??????
?????? setsockopt(socket, SOL_SOCKET, SO_LINGER,
?????????????
?????? (char *)&linger, sizeof(linger));
?
?
socket唯一性問題
正常情況下
SOCKET
套結(jié)字值是唯一的,但是操作系統(tǒng)在分配
socket
值時有隨機性,最近關(guān)閉的
socket
值可能重新分派給一個剛剛建立的新的
socket.
,尤其在大并發(fā)短連接的情況下。一個健壯的服務(wù)器
IOCP
網(wǎng)絡(luò)庫必須要考慮
socket
唯一性的問題,由于
IOCP
的排隊機制,意味著當(dāng)你調(diào)用
closesocket
關(guān)閉
socket
后,
IOCP
隊列中可能仍然堆積了該
socket
的一些
I/O completion packet
,而此時,剛關(guān)閉的
socket
值又分派給一個剛剛建立的
socket
,所以,你必須對
GetQueuedCompletionStatus
獲取到的
I/O completion packet
小心翼翼處理,避免出現(xiàn)數(shù)據(jù)混亂,然而,最好的方式等到所有
I/O completion packet
返回后才調(diào)用
closesocket
關(guān)閉該
socket
。
?