Csocket基本原理

2021-04-25 19:46:30 字數 4911 閱讀 8022

我通過幾個採用 csocket 類編寫並基於 client/server (客戶端 / 服務端)的網路聊天和傳輸檔案的程式  ,在除錯這些程式的過程中,追蹤深入至 csocket 類核心原始碼 sockcore.cpp , 對於csocket 類的執行機制可謂是一覽無遺,並且對於阻塞和非阻塞方式下的 socket 程式的編寫也是稍有體會。

閱讀本文請先注意 :

•  這裡的阻塞和非阻塞的概念僅適用於 server 端 socket 程式。

•  socket 意為套接字,他和 socket 不同,請注意首字母的大小寫。

在此之前,有必要先講述一下: 網路傳輸服務提供者, ws2_32.dll , socket 事件 和 socket window 。

1. 網路傳輸服務提供者(網路傳輸服務程序), socket 事件, socket window

網路傳輸服務提供者 ( transport service provider )是以 dll 的形式存在的,在 windows 作業系統啟動時由服務程序 svchost.exe 載入。當 socket 被建立時,呼叫 api 函式 socket (在 ws2_32.dll 中), socket 函式會傳遞三個引數 : 位址族,套接字型別 (注 2) 和協議,這三個引數決定了是由哪乙個型別的 網路傳輸服務提供者 來啟動網路傳輸服務功能。所有的網路通訊正是由網路傳輸服務提供者完成 , 這裡將 網路傳輸服務提供者 稱為 網路傳輸服務程序 更有助於理解,因為前文已提到 網路傳輸服務提供者 是由 svchost.exe 服務程序所載入的。

當 client 端 socket 和 server 端 socket 相互通訊時,兩端均會觸發 socket 事件 :

這裡僅簡要說明兩個 socket 事件 :

fd_connect: 連線事件 , 通常 client 端 socket 呼叫 socket api 函式 connect 時所觸發,這個事件發生在 client 端。

fd_accept :正在引入的連線事件,通常 server 端 socket 正在接收來自 client 端 socket 連線時觸發,這個事件發生在 server 端。

網路傳輸服務程序 將 socket 事件 儲存至 socket 的事件佇列中。

此外, 網路傳輸服務程序 還會向 socket window 傳送訊息 wm_socket_notify , 通知有 socket 事件 產生,見下文對 socket window 的周詳說明:

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

a. 將 socket 例項控制代碼和 socket 指標新增至 當前模組狀態 (注 1)的乙個對映表變數 m_pmapsockethandle 中。

b. 在 attachhandle 過程中,會 new 乙個 csocketwnd 例項 ( 基於 cwnd 派生 ) ,這裡將這個例項稱之為 socket window ,進一步理解為他是存放所有 sockets 的訊息池 ( window 訊息),請仔細檢視,這裡 socket 後多加了乙個 s ,表示建立的多個 socket 將共享乙個 訊息池 。

c. 當 client 端 socket 和 server 端相互通訊時 , 此時 網路傳輸服務程序 向 socket window 傳送訊息 wm_socket_notify ,需要說明的是 csocketwnd 視窗控制代碼儲存在 當前模組狀態 的 m_hsocketwindow 變數中。

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 事件 的訊息機制的關鍵技術。

文中存在用詞不妥和可能存在的技術問題,請大家原諒,也請批評指正,謝謝!

注:

1. 當前模組狀態

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

2. socket 型別

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

mysql的基本原理 Mysql 基本原理

mysql 基本原理 mysql是一種關聯式資料庫管理系統,關聯式資料庫將資料儲存在不同的表中,而不是將所有資料放在乙個大倉庫內,這樣就增加了速度並提高了靈活性 ysql是資料庫登入命令 uroot預設超級使用者登入 p 預設沒密碼 中寫密碼 mysqladmin uroot password 12...

8 2 1 基本原理

乙個舞台動畫物件在包含許多舞台資訊 出現在何處,佔多大面積,處在什麼角度,是否可見 這些資訊分別儲存在動畫物件的屬性中。在 中讀取這些屬性可以了解物件的位置 大小 角度等狀態資訊 修改這些屬性可以改變物件的位置 大小 角度等狀態。如果從資料的角度去理解,動畫就是在固定時間間隔點不斷修改動畫物件某項屬...

TI RFID基本原理

ti rfid是美國德州儀器公司 ti 射頻識別技術 rfid 簡稱,由於其產品的高可靠性和良好的技術二次開發環境,現在在眾多應用領域如郵政 貨運 物流 智慧型建築及小區物業管理 公路交通 工業生產線 蓄牧業等方面得到廣泛的應用。1.ti rfid技術系統組成 ti rfid的ic卡技術屬射頻 co...