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