<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    weidagang2046的專欄

    物格而后知致
    隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
    數據加載中……

    深入 CSocket 編程之阻塞和非阻塞模式

      有時,花上幾個小時閱讀、調試、跟蹤優秀的源碼程序,能夠更快地掌握某些技術關鍵點和精髓。當然,前提是對這些技術大致上有一個了解。
      我通過幾個采用 CSocket 類編寫并基于 Client/Server (客戶端 / 服務端)的網絡聊天和傳輸文件的程序 ( 詳見: 源代碼參考 ) ,在調試這些程序的過程中,追蹤深入至 CSocket 類核心源碼 Sockcore.cpp , 對于CSocket 類的運行機制可謂是一覽無遺,并且對于阻塞和非阻塞方式下的 socket 程序的編寫也是稍有體會。

    閱讀本文請先注意:

      這里的阻塞和非阻塞的概念僅適用于 Server 端 socket 程序。socket 意為套接字,它與 Socket 不同,請注意首字母的大小寫。
      客戶端與服務端的通信簡單來講:服務端 socket 負責監聽,應答,接收和發送消息,而客戶端 socket 只是連接,應答,接收,發送消息。此外,如果你對于采用 CSocket 類編寫 Client/Server 網絡程序的原理不是很了解,請先查詢一下( 詳見:參考書籍和在線幫助 )。
    在此之前,有必要先講述一下: 網絡傳輸服務提供者, 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 服務進程所加載的。
      下圖描述了網絡應用程序、 CSocket ( WSock32.dll )、 Socket API(ws2_32.dll) 和 網絡傳輸服務進程 之間的接口層次關系:



    當 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) 。該函數的作用是:

    • 將 socket 實例句柄和 socket 指針添加至 當前模塊狀態 ( 注 1 )的一個映射表變量 m_pmapSocketHandle 中。
    • 在 AttachHandle 過程中,會 new 一個 CSocketWnd 實例 ( 基于 CWnd 派生 ) ,這里將這個實例稱之為 socket window ,進一步理解為它是存放所有 sockets 的消息池 ( window 消息),請仔細查看,這里 socket 后多加了一個 s ,表示創建的多個 socket 將共享一個 消息池 。
    • 當 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(...))
    { 
         // The socket is marked as nonblocking and no connections are present to be accepted. 
    	if (GetLastError() == WSAEWOULDBLOCK) 
     PumpMessage(FD_ACCEPT); else return FALSE; }
      它不斷調用 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) 
    { 
    	CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam); 
    	CSocket::ProcessAuxQueue(); 
    	return 0L ; 
    }
    
      消息參數 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 ,它是不可靠的連接方式。

    源代碼參考:

    1. http://www.codeproject.com/internet/SocketFileTransfer.asp 采用 CSocket 類編寫的基于 Client/Server 的網絡文件傳輸程序,它是基于阻塞模式的 Client/Server 端網絡程序典型示例。
    2. http://www.codeguru.com/Cpp/I-N/network/messaging/article.php/c5453 采用 CSocket 類編寫的基于 Client/Server 的網絡聊天程序,它是基于非阻塞模式的 Client/Server 端網絡程序典型示例。

    參考資料:

    • Microsoft MSDN Library – January 2001
    • 《Windows 網絡編程》 清華大學出版社

    from: http://www.vckbase.com/document/viewdoc/?id=1375

    posted on 2006-10-03 20:01 weidagang2046 閱讀(525) 評論(0)  編輯  收藏 所屬分類: Windows

    主站蜘蛛池模板: 亚洲一区二区影视| 亚洲一级毛片免费在线观看| 国产亚洲综合一区二区三区| 日本黄色免费观看| 国产成人亚洲午夜电影| 九九久久精品国产免费看小说 | 亚洲理论片在线中文字幕| 亚洲中文字幕久久精品无码喷水 | 亚洲精品WWW久久久久久 | 黑人粗长大战亚洲女2021国产精品成人免费视频 | 久久久综合亚洲色一区二区三区| 国产一精品一AV一免费| 亚洲国产香蕉碰碰人人| 日本XXX黄区免费看| 狠狠综合亚洲综合亚洲色| 亚洲片国产一区一级在线观看 | 亚洲伊人久久精品| 成人免费网站在线观看| 老司机免费午夜精品视频| 久久亚洲高清综合| 99在线视频免费| 亚洲色丰满少妇高潮18p| 成人亚洲网站www在线观看| 国产在线国偷精品免费看| 4444亚洲国产成人精品| 超pen个人视频国产免费观看| 特级毛片A级毛片100免费播放| 亚洲精品二区国产综合野狼| 久久笫一福利免费导航| 免费夜色污私人影院网站| 国产aⅴ无码专区亚洲av| 黄瓜视频高清在线看免费下载| 免费国产黄网站在线观看动图| 亚洲阿v天堂在线| www.999精品视频观看免费| 日本视频免费观看| 亚洲春色另类小说| 国产成人亚洲综合| 成年大片免费视频| 四虎永久在线精品视频免费观看| 日本高清免费不卡在线|