本文解釋如何組合 CSocket 對象、CSocketFile 對象和 CArchive 對象以簡化通過 Windows 套接字發送和接收數據。
文章 Windows Sockets:帶存檔的套接字示例介紹了 PacketSerialize 函數。 PacketSerialize 示例中的存檔對象的工作機制與傳遞給 MFC Serialize 函數的存檔對象非常相似。它們之間的基本差異是:對於套接字,存檔並不附加到標准的 CFile 對象(通常與磁盤文件關聯),而是附加到 CSocketFile 對象。 CSocketFile 對象不是連接到磁盤文件,而是連接到 CSocket 對象。
一個 CArchive 對象管理一個緩沖區。當存儲(發送)存檔的緩沖區已滿時,關聯的 CFile 對象寫出緩沖區的內容。刷新附加到套接字的存檔緩沖區相當於發送消息。當加載(接收)存檔的緩沖區已滿時, CFile 對象停止讀取直到該緩沖區再次可用。
CSocketFile 類從 CFile 派生,但它並不支持 CFile 成員函數,如定位函數 Seek 、 GetLength 、 SetLength 等,鎖定函數 LockRange 和 UnlockRange ,或 GetPosition 函數。每個 CSocketFile 對象必須要做的事是,將字節序列寫入或讀入關聯的 CSocket 對象,或從此對象寫出或讀出字節序列。因為不涉及文件, Seek 和 GetPosition 等操作沒有意義。 CSocketFile 從 CFile 派生,因此它通常會繼承所有這些成員函數。為防止發生這種情況,在 CSocketFile 中重寫不受支持的 CFile 成員函數以引發 CNotSupportedException。
CSocketFile 對象調用其 CSocket 對象的成員函數來發送或接收數據。
下圖顯示了在通信的兩端這些對象之間的關系。
CArchive、CSocketFile 和 CSocket
這看起來很復雜,其目的是使您不必親自管理套接字的細節。您創建套接字、文件和存檔,然後通過將數據插入存檔或從存檔提取數據,開始發送或接收數據。CArchive、CSocketFile 和 CSocket 管理後台的細節。
CSocket 對象實際是一個兩狀態對象:有時異步(通常狀態)有時同步。處於異步狀態時,套接字可以從框架接收異步通知。然而,在操作(如接收或發送數據)過程中,套接字變為同步的。這意味著在同步操作完成之前,套接字不會接收進一步的異步通知。由於套接字切換模式,請執行類似下面的操作:
CMySocket::OnReceive( ){ // ... ar > > str; // ...}
如果 CSocket 沒有實現為兩狀態對象,則在您處理前面通知的同時,有可能接收到同類事件的附加通知。例如,在處理
OnReceive
時,可能收到
OnReceive
通知。在上面的代碼片段中,從存檔提取
str
可能導致遞歸。通過切換狀態, CSocket 用防止附加通知的方法防止遞歸。一般規則是通知內沒有通知。
CHATTER 和 CHATSRVR 示例應用程序闡釋了這種用法。有關 MFC 示例的源代碼和信息,請參見 MFC 示例。
注意 CSocketFile 也可以作為一個沒有 CArchive 對象的(有限)文件使用。默認情況下, CSocketFile 構造函數的 bArchiveCompatible 參數為 TRUE 。這指定文件對象用於存檔。若要使用沒有存檔的文件對象,請在 bArchiveCompatible 參數中傳遞 FALSE 。
在“存檔兼容”模式下, CSocketFile 對象可提供更好的性能並能減少“死鎖”的危險。當發送套接字和接收套接字都在等待對方或等待公共資源時,就會發生死鎖現象。如果 CArchive 對象用處理 CFile 對象的方式處理 CSocketFile ,也可能發生這種情況。處理 CFile 時,存檔可假定只要它接收到的字節數比所請求的少,則說明已到達文件尾。而處理 CSocketFile 時,數據是基於消息的,緩沖區可包含多條消息,因此,接收的字節數比請求的字節數少並不能說明已到達文件尾。應用程序在此情況下並不阻塞(而使用 CFile 時可能阻塞),它可繼續從緩沖區讀取消息直到緩沖區變空。在這種情況下, CArchive 中的 IsBufferEmpty 函數有助於監視存檔緩沖區的狀態。
有關更多信息,請參見 Windows Sockets:使用帶存檔的套接字。