完成端口->接受鏈接請求(收集)

完成端口->接受鏈接請求(收集)

接受鏈接請求 html

服務器要作的最普通的事情之一就是接受來自客戶端的鏈接請求。在套接字上使用重疊I/O接受鏈接的唯一API就是AcceptEx()函數。有趣的是,一般的同步接受函數accept()的返回值是一個新的套接字,而AcceptEx()函數則須要另一個套接字做爲它的參數之一。這是由於AcceptEx()是一個重疊操做,因此你須要事先建立一個套接字(但不要綁定或鏈接它),並把這個套接字經過參數傳給AcceptEx()。如下是一小段典型的使用AcceptEx()的僞代碼: 服務器


do {
    -等待上一個 AcceptEx 完成
    -建立一個新套接字並與完成端口進行關聯
    -設置背景結構等等
    -發出一個 AcceptEx 請求
}while(TRUE);
數據結構

做爲一個高響應能力的服務器,它必須發出足夠的AcceptEx調用,守候着,一旦出現客戶端鏈接請求就馬上響應。至於發出多少個AcceptEx纔夠,就取決於你的服務器程序所期待的通訊交通類型。好比,若是進入鏈接率高的狀況(由於鏈接持續時間較短,或者出現交通高峯),那麼所須要守候的AcceptEx固然要比那些偶爾進入的客戶端鏈接的狀況要多。聰明的作法是,由應用程序來分析交通情況,並調整AcceptEx守候的數量,而不是固定在某個數量上。 架構

對於Windows2000,Winsock提供了一些機制,幫助你斷定AcceptEx的數量是否足夠。這就是,在建立監聽套接字時建立一個事件,經過WSAEventSelect()這個API並註冊FD_ACCEPT事件通知來把套接字和這個事件關聯起來。一旦系統收到一個鏈接請求,若是系統中沒有AcceptEx()正在等待接受鏈接,那麼上面的事件將收到一個信號。經過這個事件,你就能夠判斷你有沒有發出足夠的AcceptEx(),或者檢測出一個非正常的客戶請求(下文述)。這種機制對Windows NT 4.0不適用。 socket

使用AcceptEx()的一大好處是,你能夠經過一次調用就完成接受客戶端鏈接請求和接受數據(經過傳送lpOutputBuffer參數)兩件事情。也就是說,若是客戶端在發出鏈接的同時傳輸數據,你的AcceptEx()調用在鏈接建立並接收了客戶端數據後就能夠馬上返回。這樣多是頗有用的,可是也可能會引起問題,由於AcceptEx()必須等所有客戶端數據都收到了才返回。具體來講,若是你在發出AcceptEx()調用的同時傳遞了lpOutputBuffer參數,那麼AcceptEx()再也不是一項原子型的操做,而是分紅了兩步:接受客戶鏈接,等待接收數據。當缺乏一種機制來通知你的應用程序所發生的這種狀況:「鏈接已經創建了,正在等待客戶端數據」,這將意味着有可能出現客戶端只發出鏈接請求,可是不發送數據。若是你的服務器收到太多這種類型的鏈接時,它將拒絕鏈接更多的合法客戶端請求。這就是黑客進行「拒絕服務」攻擊的常見手法。 函數

要預防此類攻擊,接受鏈接的線程應該不時地經過調用getsockopt()函數(選項參數爲SO_CONNECT_TIME)來檢查AcceptEx()裏守候的套接字。getsockopt()函數的選項值將被設置爲套接字被鏈接的時間,或者設置爲-1(表明套接字還沒有創建鏈接)。這時,WSAEventSelect()的特性就能夠很好地利用來作這種檢查。若是發現鏈接已經創建,可是好久都沒有收到數據的狀況,那麼就應該終止鏈接,方法就是關閉做爲參數提供給AcceptEx()的那個套接字。注意,在多數非緊急狀況下,若是套接字已經傳遞給AcceptEx()並開始守候,但還未創建鏈接,那麼你的應用程序不該該關閉它們。這是由於即便關閉了這些套接字,出於提升系統性能的考慮,在鏈接進入以前,或者監聽套接字自身被關閉以前,相應的內核模式的數據結構也不會被幹淨地清除。 post

發出AcceptEx()調用的線程,彷佛與那個進行完成端口關聯操做、處理其它I/O完成通知的線程是同一個,可是,別忘記線程裏應該盡力避免執行阻塞型的操做。Winsock2分層結構的一個反作用是調用socket()或WSASocket() API的上層架構可能很重要(譯者不太明白原文意思,抱歉)。每一個AcceptEx()調用都須要建立一個新套接字,因此最好有一個獨立的線程專門調用AcceptEx(),而不參與其它I/O處理。你也能夠利用這個線程來執行其它任務,好比事件記錄。 性能

有關AcceptEx()的最後一個注意事項:要實現這些API,並不須要其它提供商提供的Winsock2實現。這一點對微軟特有的其它API也一樣適用,好比TransmitFile()和GetAcceptExSockAddrs(),以及其它可能會被加入到新版Windows的API. 在Windows NT和2000上,這些API是在微軟的底層提供者DLL(mswsock.dll)中實現的,可經過與mswsock.lib編譯鏈接進行調用,或者經過WSAIoctl() (選項參數爲SIO_GET_EXTENSION_FUNCTION_POINTER)動態得到函數的指針。 spa

若是在沒有事先得到函數指針的狀況下直接調用函數(也就是說,編譯時靜態鏈接mswsock.lib,在程序中直接調用函數),那麼性能將很受影響。由於AcceptEx()被置於Winsock2架構以外,每次調用時它都被迫經過WSAIoctl()取得函數指針。要避免這種性能損失,須要使用這些API的應用程序應該經過調用WSAIoctl()直接從底層的提供者那裏取得函數的指針。 線程

相關文章
相關標籤/搜索