編寫完成埠網路伺服器的一些說明 1

2021-04-01 15:20:54 字數 3454 閱讀 1234

1. acceptex:

用來發起乙個非同步的呼叫, 接受客戶端將要發出的連線請求. 與 accept 不同的是, 你必須先手動建立乙個 socket 提供給 acceptex, 用

來接受連線 ( accept 是內在地建立乙個 socket 接受連線, 並返回值 ). 而且, accept 建立的 socket 會自動繼承監聽 socket 的屬性,

acceptex 卻不會. 因此如果有必要, 在 acceptex 成功接受了乙個連線之後, 我們必須呼叫:

setsockopt( hacceptsocket, sol_socket, so_update_accept_context,  ( char* )&( hlistensocket ), sizeof( hlistensocket ) );

來做到這一點.

acceptex 允許在接受連線的同時接收對方發來的第一組資料, 這當然是出於效能的考慮. 但是這時候 acceptex 最少要接收到乙個位元組的

資料才會返回, 一旦碰到惡意連線它就永遠不會返回了. 關閉這項功能的方式是: 把引數 dwreceivedatalength 至為 0, 開啟則相反. 當然了

, 如果一定要啟用這個功能, 我們也有防禦的辦法. 啟動乙個執行緒定時地檢測每乙個 acceptex 是否已經連線, 連線時間為多久, 以此判斷對

方是否是****:

int isecs;

int ibytes = sizeof( int );

getsockopt( hacceptsocket, sol_socket, so_connect_time, (char *)&isecs, &ibytes );

isecs 為 -1 表示還未建立連線, 否則就是已經連線的時間.

呼叫 acceptex 的方式:

#include // for wsaid_acceptex

typedef bool ( winapi * pfnacceptex ) ( socket, socket, pvoid, dword, dword, dword, lpdword, 

一旦 acceptex 呼叫完成 ( 通過完成埠通知你 ), 接下來的步驟就是 1. 上面講的 so_update_accept_context;

2. 將 hacceptsocket

繫結到完成埠.

2. tran**itfile

tran**itfile 顧名思義是用來進行檔案傳輸的函式, 全自動無需干涉的. 在 win nt 專業版/家庭版上無法發揮全部效能. 在這裡只討論它

lptran**it_file_buffers, dword );

pfnacceptex pfntran**itfile;

dword dwbytes;

guid guidtran**itfile = wsaid_tran**itfile;

::wsaioctl( hlistensocket, sio_get_extension_function_pointer, &guidtran**itfile,

sizeof( guidtran**itfile ), &pfntran**itfile,

sizeof( pfntran**itfile ), &dwbytes, null, null );

pfntran**itfile( hacceptsocket, null, 0, 0, null, null, tf_disconnect | tf_reuse_socket );

經過這個函式處理的 socket 可以作為接受連線的 socket 提交給 acceptex 再次使用. 當這樣的 socket 接受連線成功後, 如果往完成

埠上繫結會出錯 - 因為上次接受連線成功時已經繫結過了, 這個錯誤可以忽略.

3. fd_accept

即使我們在程式啟動時發起了再多的 acceptex , 也有可能碰到數目不夠使用者連不上來的情況. 在 win2000 或更高版本的系統上, 我們可

以通過 wsaeventselect 註冊乙個 fd_accept 事件. 當 acceptex 數目不足以應付大量的連線請求時, 這個事件會被觸發. 於是我們就可以發

出更多的 acceptex, 而且我們還可以抽空辨別一下 acceptex 為什麼這麼快就用光了, 是不是碰上攻擊者了( 辨別方法見上文所述 ) ?

handle hacceptexthreadevent = ::createevent( null, true, false, _t("acceptexthreadevent") );

::wsaeventselect( hlistensocket, hacceptexthreadevent, fd_accept );

dword winapi acceptexthread( lpvoid lpparameter )

while( true )

::resetevent( hacceptexthreadevent );

if( m_sbwaitforexit )

pfnacceptex( hlistensocket, ... );

//// ... 在此檢查是否被攻擊//}

return 0;

}要說明的是, wsaeventselect() 所需的 wsaevent 和 createevent() 所建立的 event 是通用的.

4. wsasend 和 wsarecv

預設情況下, 每乙個 socket 在系統底層都擁有乙個傳送和接收緩衝區.

我們傳送資料時, 實際上是把資料複製到傳送緩衝區中, 然後 wsasend 返回. 但是如果傳送緩衝區已經滿了, 那麼我們在 wsasend 中指

定的緩衝區就會被鎖定到系統的非分頁記憶體池中, wsasend 返回 wsa_io_pending. 一旦網路空閒下來, 資料將會從我們提交的緩衝區中直接被

傳送出去, 與 " 我們的緩衝區->傳送緩衝區->網路 " 相比節省了一次複製操作.

wsarecv 也是一樣, 接收資料時直接把接收緩衝區中的資料複製出來. 如果接收緩衝區中沒有資料, 我們在 wsarecv 中指定的緩衝區就會

被鎖定到系統的非分頁記憶體池以等待資料, wsarecv 返回 wsa_io_pending. 如果網路上有資料來了, 這些資料將會被直接儲存到我們提供的緩

沖區中.

如果乙個伺服器同時連線了許多客戶端, 對每個客戶端又呼叫了許多 wsarecv, 那麼大量的記憶體將會被鎖定到非分頁記憶體池. 鎖定這些記憶體

時是按照頁面邊界來鎖定的, 也就是說即使你 wsarecv 的快取大小是 1 位元組, 被鎖定的記憶體也將會是 4k. 非分頁記憶體池是由整個系統共用的

, 如果用完的話最壞的情況就是系統崩潰. 乙個解決辦法是, 使用大小為 0 的緩衝區呼叫 wsarecv. 等到呼叫成功時再換用非阻塞的 recv 接

收到來的資料, 直到它返回 wsaewouldblock 表明資料已經全部讀完. 在這個過程中沒有任何記憶體需要被鎖定, 但壞處是效率稍低.

編寫完成埠網路伺服器的一些說明

編寫完成埠網路伺服器的一些說明 1 1.acceptex 用來發起乙個非同步的呼叫,接受客戶端將要發出的連線請求.與 accept 不同的是,你必須先手動建立乙個 socket 提供給 acceptex,用 來接受連線 accept 是內在地建立乙個 socket 接受連線,並返回值 而且,acce...

python 網路伺服器的建立

建立伺服器連線的步驟 1 建立socket物件 s socket.socket socket.af inet,socket.sock stream 2 設定socket選項 可選的 s.setsockopt socket.sol socket,socket.so reuseaddr,1 s0 reu...

基於libevent的網路伺服器模型

本模型中採用了多執行緒技術,主線程和子執行緒之間通過管道進行通訊。伺服器有主線程和一組工作執行緒,其中主線程只負責監聽客戶端的鏈結請求,並將請求平均的分配給工作執行緒。工作執行緒負責處理與客戶端的鏈結以及相關的業務。每個子執行緒維護乙個連線佇列,每乙個連線有乙個反饋的佇列。工作執行緒在初始化的過程中...