深入 CSocket 程式設計之阻塞和非阻塞模式

2021-04-01 20:32:18 字數 3205 閱讀 1009

網路傳輸服務程序 將 socket 事件 儲存至 socket 的事件佇列中。此外, 網路傳輸服務程序 還會向 socket window 傳送訊息 wm_socket_notify , 通知有 socket 事件 產生,見下文對 socket window 的詳細說明。

呼叫 csocket::create 函式後,socket 被建立。 socket 建立過程中呼叫 casyncsocket::attachhandle(socket hsocket, casyncsocket* psocket, bool bdead) 。該函式的作用是:

2、阻塞模式

阻塞模式下 server 端與 client 端之間的通訊處於同步狀態下。在 server 端直接例項化 csocket 類,呼叫 create 方法建立 socket ,然後呼叫方法 listen 開始偵聽,最後用乙個 while 迴圈阻塞呼叫 accept 函式用於等待來自 client 端的連線,如果這個 socket 在主線程(主程式)中執行,這將導致主線程的阻塞。因此,需要建立乙個新的執行緒以執行 socket 服務。

除錯跟蹤至 csocket::accept 函式原始碼:

while(!accept(...))

它不斷呼叫 casyncsocket::accept ( csocket 派生自 casyncsocket 類)判斷 server 端 socket 的事件佇列中是否存在正在引入的連線事件 - fd_accept (見 1 ),換句話說,就是判斷是否有來自 client 端 socket 的連線請求。

如果當前 server 端 socket 的事件佇列中存在正在引入的連線事件, accept 返回乙個非 0 值。否則, accept 返回 0,此時呼叫 getlasterror 將返回錯誤** wsaewouldblock ,表示佇列中無任何連線請求。注意到在迴圈體內有一句**:

pumpmessage(fd_accept);
pumpmessage 作為乙個訊息幫浦使得 socket window 中的訊息能夠維持在活動狀態。實際跟蹤進入 pumpmessage 中,發現這個訊息幫浦與 accept 函式的呼叫並不相關,它只是使很少的 socket window 訊息(典型的是 wm_paint 視窗重繪訊息)處於活動狀態,而絕大部分的 socket window 訊息被阻塞,被阻塞的訊息中含有 wm_socket_notify。

很顯然,如果沒有來自 client 端 socket 的連線請求, csocket 就會不斷呼叫 accept 產生迴圈阻塞,直到有來自 client 端 socket 的連線請求而解除阻塞。

阻塞解除後,表示 server 端 socket 和 client 端 socket 已成功連線, server 端與 client 端彼此相互呼叫 send 和 receive 方法開始通訊。

3、非阻塞模式

在非阻塞模式下 利用 socket 事件 的訊息機制, server 端與 client 端之間的通訊處於非同步狀態下。

通常需要從 csocket 類派生乙個新類,派生新類的目的是過載 socket 事件 的訊息函式,然後在 socket 事件 的訊息函式中添入合適的**以完成 client 端與 server 端之間的通訊,與阻塞模式相比,非阻塞模式無需建立乙個新執行緒。

這裡將討論當 server 端 socket 事件 - fd_accept 被觸發後,該事件的處理函式 onaccept 是如何進一步被觸發的。其它事件的處理函式如 onconnect, onreceive 等的觸發方式與此類似。

在 1 中已提到 client/server 端通訊時, server 端 socket 正在接收來自 client 端 socket 連線請求,這將會觸發 fd_accept 事件,同時 server 端的 網路傳輸服務程序 向 server 端的 socket window (csocketwnd )傳送事件通知訊息 wm_socket_notify , 通知有 fd_accept 事件產生 , csocketwnd 在收到事件通知訊息後,呼叫訊息處理函式 onsocketnotify:

lresult csocketwnd::onsocketnotify(wparam wparam, lparam lparam) 

訊息引數 wparam 是 socket 的控制代碼, lparam 是 socket 事件 。這裡稍作解釋一下,csocketwnd 類是作為 csocket 類的 友元類 ,這意味著它可以訪問 csocket 類中的保護和私有成員函式和變數, auxqueueadd 和 processauxqueue 是 csocket 類的靜態成員函式,如果你對友元不熟悉,請迅速找本有關 c++ 書看一下友元的使用方法吧!

processauxqueue 是實質處理 socket 事件的函式,在該函式中有這樣一句**:

casyncsocket* psocket = casyncsocket::lookuphandle((socket)wparam, true);
其實也就是由 socket 控制代碼得到傳送事件通知訊息的 socket 指標 psocket:從 m_pmapsockethandle 中查詢(見 1 )!

最後, wsagetselectevent(lparam) 會取出事件型別,在乙個簡單的 switch 語句中判斷事件型別並呼叫事件處理函式。在這裡,事件型別是 fd_accept ,當然就呼叫 psocket->onaccept !

結束語

server 端 socket 處於阻塞呼叫模式下,它必須在乙個新建立的執行緒中工作,防止主線程被阻塞。

當有多個 client 端 socket 與 server 端 socket 連線及通訊時, server 端採用阻塞模式就顯得不適合了,應該採用非阻塞模式 , 利用 socket 事件 的訊息機制來接受多個 client 端 socket 的連線請求並進行通訊。

在非阻塞模式下,利用 csocketwnd 作為所有 sockets 的訊息池,是實現 socket 事件 的訊息機制的關鍵技術。文中存在用詞不妥和可能存在的技術問題,請大家原諒,也請批評指正,謝謝!

注:當前模組狀態——用於儲存當前執行緒和模組狀態的乙個結構,可以通過 afxgetthreadmodule() 獲得。afx_module_thread_state 在 csocket 重新定義為 _afx_sock_thread_state 。

socket 型別——在 tcp/ip 協議中, client/server 網路程式採用 tcp 協議:即 socket 型別為 sock_stream ,它是可靠的連線方式。在這裡不採用 udp 協議:即 socket 型別為 sock_dgram ,它是不可靠的連線方式。

c socket程式設計之ftp

class ftp class region 連線 連線 private void connect string path 連線ftp endregion region ftp登入資訊 ftp登入資訊 ftpserverip ftpuserid ftppassword public void ftp...

c socket程式設計之美

一 ip位址操作類 1 ipaddress類 a 在該類中有乙個parse方法,可以把點分的十進位制ip表示轉化成ipaddress類,方法如下 ipaddress address ipaddress.parse 192.168.0.1 b ipaddress提供4個唯讀字段 any 用於代表本地系...

C Socket程式設計之小試牛刀

名義上學了c 已經1年多了,從底層api玩起到高層mfc的使用,基於cocos2d x遊戲引擎也算是開發過幾款端遊和一些手遊了。不過那段時間做的都是單機遊戲,對於c 網路這塊幾乎沒有接觸過,今天突然有此想法,就利用一點時間玩了下socket程式設計,做了個建議的單向聊天demo。先來個效果圖,介面確...