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