MFC--網路程式設計之CAsyncSocket詳解
原文連結地址:https://blog.csdn.net/u012372584/article/details/76146844
CAsyncSocket類是從Object類派生而來。CAsyncSocket物件稱為非同步套接字物件
使用CAsyncSocket進行網路程式設計,可以充分利用Windows作業系統提供的訊息驅動機制,通過應用程式框架來傳遞訊息,方便地處理各種網路事件。另一方面,作為MFC微軟基礎類庫中的一員,CAsyncSocket可以和MFC的其他類融為一體,大大擴充套件了網路程式設計的空間,方便了程式設計。
1. 使用CAsyncSocket的一般步驟
網路應用程式一般採用客戶端/伺服器模式,他們使用的CAsyncSocket程式設計有所不同,下面以表格的形式方式看一下伺服器和客戶端之間的不同
序號 | 服務端 | 客戶端 |
1 |
構造一個套接字 CAsyncSocket sockServer |
構造一個套接字 CAsyncSocket sockClient |
2 |
建立SOCKET控制程式碼,繫結到指定的埠 sockServer.Create(nPort); |
建立SOCKET控制程式碼,使用預設引數 sockClient.Create(); |
3 |
啟動監聽,時刻準備接收連線請求 sockServer.Listen(); |
|
4 |
請求連結伺服器 sockClient.Connect(strAddress,nPort) |
|
5 |
構造一個新的空套接字 CAsyncSocket sockRecv; 接收連線 sockServer.Accept(sockRecv); |
|
6 |
接收資料 sockRecv.Receive(pBuffer,nLen); |
傳送連線 sockClient.Send(pBuffer,nLen); |
7 |
傳送資料 sockRecv.Send(pBuffer,nLen); |
接收資料 sockClient.Receive(pBuffer,nLen); |
8 |
關閉套接字物件 sockRecv.Close(); |
關閉套接字物件 sockClient.Close( |
ps:客戶端與服務端都要首先構造一個CAsyncSocket物件,然後使用該物件的Create成員函式來建立底層的SOCKET控制程式碼。伺服器端要繫結到特定的埠
對於伺服器端的套接字物件,應使用CAsyncSocket::Listen函式進行監聽狀態,一旦收到來自客戶端的連結請求,就呼叫CAsyncSocket::Accept來接收。對於客戶端的套接字物件,應當使用CAsyncSocket::Connect來連線到一個伺服器端的套接字物件。建立連結之後,雙方就可以按照應用層協議交換資料了。
這裡需要注意,Accept是將一個新的空CAsyncSocket物件作為它的引數,在呼叫Accept之前必須構造這個物件。與客戶端套接字的連線是通過它建立的,如果這個套接字物件退出,連線也就關閉。對於這個新的套接字物件,不需要呼叫Create來建立它的底層套接字
呼叫CAsyncSocket物件的其他成員函式,如Send和Receive執行與其他套接字物件的通訊,這些成員函式與Windows Sockets API函式在形式和用法上基本是一致的。
關閉並銷燬CAsyncSocket物件。如果在堆疊上建立了套接字物件,當包含此物件的函式退出時,會呼叫該類的解構函式,銷燬該物件。在銷燬該物件之前,解構函式會呼叫該物件的Close成員函式。如果在堆上使用new建立了套接字物件,可先呼叫Close成員函式關閉它,在使用delete來刪除釋放該物件
2.在使用CAsyncSocket進行網路通訊時,我們還需要處理以下幾個問題:
① 堵塞處理,CAsyncSocket物件專用於非同步操作,不支援堵塞工作模式,如果應用程式需要支援堵塞操作,必須自己解決
② 位元組順序的轉換。在不同的結構型別的計算機之間進行資料傳輸時,可能會有計算機之間位元組儲存順序不一致的情況。使用者程式需要自己對不用的位元組順序進行轉換
③ 字串轉換。同樣,不同結構型別的計算機的字串儲存順序也可能不同,需要自行轉換,如Unicode和ANSI字串之間的轉換
3. 分步驟詳解
建立CAsyncSocket物件
建立非同步套接字物件一般是分為兩個步驟,首先要構造CAsyncSocket物件,其次建立該物件底層的SOCKET控制程式碼
(1)建立空的CAsyncSocket物件
通過呼叫CAsyncSocket建構函式,建立一個新的空CAsyncSocket套接字物件,建構函式還帶引數。套接字物件建立之後必須呼叫他的成員函式來建立底層的套接字資料結構,並繫結他的地址
方法一:
CAsyncSocket Sock
Sock.Create(...)
方法二:
CAsyncSocket *pSock = new CAsyncSocket;
pSock->Create(...);
delete pSock;
pSock =NULL;
ps:之前見過很多朋友釋放指標的時候總是delete就完事了,往往不知正在給自己的程式帶來前所未有的災難,而此時的指標我們稱之為“野指標”,注意:野指標不是NULL指標,而是不可用記憶體的指標,即垃圾指標;野指標是很危險的,因為我們無法通過if去判斷指標是正常指標還是野指標,所以,我們在書寫程式碼時一定要養成良好的程式設計習慣!!!避免野指標的方法我們可以通過以下幾點:
① 宣告指標一定要初始化,如果不初始化為NULL,那麼此時一定要指向一塊合法的記憶體
② 當呼叫delete或者free去釋放指標後,一定要將指標重新指向NULL,可以參照SkinUI的SafeDelete
③ 指標操作超出了變數的作用範圍,比如如下程式碼,
class A
{
void Fun(){}
};
class B
{
A *m_A;
B(){m_A=NULL;}
void Fun()
{
A a
m_A =&a;
//注意變數A的生命週期,當該方法執行完畢後,A會被釋放,此時m_A就變成了無效的野指標
}
void Fun1()
{
m_A->Fun(); //m_A為野指標,到這裡也就出現了錯誤
}
};
通過上述方法可以大大的降低程式碼出現野指標的風險。
(2)建立CAsyncSocket套接字的底層套接字控制程式碼
通過CAsyncSocket::Create建立該物件的底層套接字控制程式碼,決定套接字物件的具體特性。
函式原型如下:
BOOL Create(
UINT nSocketPort = 0,
int nSocketType = SOCK_STREAM,
long lEvent = FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,
LPCTSTR lpszSocketAddress = NULL
);
引數:
nSocketPort,指定了一個分配給套接字的傳輸層埠號,預設值為0,表示讓系統為這個套接字分配一個自由的埠號。但是對於伺服器應用程式而言,一般都需要事先分配一個公認的埠號,所以切記,伺服器應用程式呼叫此函式時,必須分配一個埠號
nSocketType,套接字的型別,當指定為SOCK_STREAM時表示生成流式套接字,若使用SOCK_DGRAM表示生成資料包套接字
lEvent,指定為CAsyncSocket物件生成通知訊息的套接字事件,預設對所有的套接字事件都生成通知訊息
lpszSocketAddress,指定套接字的網路地址,對Internet通訊域來說,就是主機的域名或者ip地址,比如www.gymsaga.com或123.123.123.123。如果使用預設值,表示使用預設的本機ip地址
4 關於CAsyncSocket可以接受並處理的訊息事件
在CAsyncSocket::Create中,引數lEvent指定了為CAsyncSocket物件生成通知訊息的套接字事件,最能體現CAsyncSocket對Windows訊息驅動機制的支援
先認識一下這六種相關事件和通知訊息
關於lEvent引數的符號常量,我們可以在WinSock中找到
/*
* Define flags to be used with the WSAAsyncSelect() call.
*/
#define FD_READ 0x01
#define FD_WRITE 0x02
#define FD_OOB 0x04
#define FD_ACCEPT 0x08
#define FD_CONNECT 0x10
#define FD_CLOSE 0x20
它們代表了MFC套接字物件可以接收並處理的6種網路事件,當事件發生時,套接字物件會收到相應的通知訊息,並自動執行套接字物件響應的事件處理函式
1:FD_READ : 通知有資料可讀。當一個套接字物件的資料輸入緩衝區收到其他套接字物件傳送來的資料時,發生此事件,並通過該套接字物件 ,告訴它可以呼叫Receive成員來接收資料
2:FD_WRITE: 通知可以寫資料,當一個套接字物件的資料輸出緩衝區中的資料已經傳送出去,輸出緩衝區已騰空時,發生此事件,並通過該套接字物件,告訴它可以呼叫Send函式向外傳送資料
3:FD_ACCEPT: 通知監聽套接字有連線請求可以接收。當客戶端的連結請求到達伺服器時,進一步說,是當客戶端的連線請求已經進入伺服器監聽套接字的接收緩衝區佇列時,發生此事件,並通過監聽套接字物件,告訴它可以呼叫Accept成員來接收待決的連結請求。這個事件僅對流式套接字有效,並且發生在伺服器端
4:FD_CONNECT: 通知請求連結的套接字,連結的要求已經被處理。當客戶端的連線請求已被處理時,發生此事件。存在兩種情況:一種是伺服器端已接收了連結請求,雙方的連線已經建立,通知客戶端套接字,可以使用連結來傳輸資料了;另一種情況是連結請求被拒絕,通知客戶機套接字,它所請求的連線失敗。這個事件僅對流式套接字有效,並且發生在客戶端
5:FD_CLOSE: 通知套接字已關閉。當連結的套接字關閉時發生
6:FD_OOB: 通知將帶外資料到達。當對方的流失套接字傳送帶外資料時,發生此事件,並通知接收套接字,正在傳送的套接字有帶外資料要求傳送,帶外資料是有沒對連結的流失套接字相關的在邏輯上獨立的通道,帶外資料通道典型的是用來傳送緊急資料。MFC支援帶外有資料,使用CAsyncSocket類的高階使用者可能需要使用帶外資料通道,但不鼓勵使用CSocket類的使用者使用它,更容易的方法是建立第二個套接字來傳送這樣的資料
MFC框架對這六種事件的處理
當上述的網路事件發生時,MFC框架做何處理呢?MFC框架按照Windows系統的訊息驅動把訊息傳送給相應的套接字物件,並呼叫作為該物件函式的事件處理函式,事件與處理函式一一對映。
在afxSock.h中我們可以找到CAsyncSocket類對這六種對應事件的處理函式
// Overridable callbacks
protected:
virtual void OnReceive(int nErrorCode);
virtual void OnSend(int nErrorCode);
virtual void OnOutOfBandData(int nErrorCode);
virtual void OnAccept(int nErrorCode);
virtual void OnConnect(int nErrorCode);
virtual void OnClose(int nErrorCode);
其中引數nErrorCode的值,是在函式被呼叫時,由MFC框架提供的,表明套接字最新的狀況,如果是0,說明成功,如果為非零值,說明套接字物件有某種錯誤
當某個網路事件發生時,MFC框架會自動呼叫套接字物件對應的事件處理函式。這就相當於給套接字物件一個通知,告訴它某個重要的事件已經發生,所以也稱為套接字類的通知函式或者回撥函式
5.過載套接字物件的回撥函式
在程式設計中,一般我們不會直接去使用CAsyncSocket或者CSocket,而是從他們派生出自己的套接字類來。然後在派生類中對這些虛擬函式進行過載處理,加入應用程式對於網路事件處理的特定程式碼
如果是從CAsyncSocket類派生了自己的套接字類,就必須過載該應用程式所感興趣的那些網路事件所對應的通知函式。如果從CSocket類派生一個類,是否過載所感興趣的通知函式則由自己決定。也可以使用CSocket類本身的回撥函式,但預設情況下,CSocket本身的回撥函式什麼也不做,只是一個空函式。
MFC框架自動呼叫通知函式,使得使用者可以在套接字被通知的時候來優化套接字的行為。例如,使用者可以從自己的OnReceive通知函式中呼叫套接字物件的成員函式Receive,就是說,在被通知的時候,已經有資料可讀了,才呼叫Receive來讀取它。這個方法不是必須的,但它是一個有效的方案。此外,也可以使用自己的通知函式跟蹤程式,列印TRACE訊息等
對於CSocket物件,還有如下一些不同之處
在一個諸如接收或者傳送資料的操作期間,一個CSocket物件成為同步的,在同步狀態期間,在當前套接字等待它想要的通知時,任何的為其他套接字的通知被排成佇列,一旦該套接字完成了它的同步操作,並再次成為非同步的,其他的套接字才可以開始接收排列的通知
重要的一點是:在CSocket中,從來不呼叫OnConncet通知函式,對於連線,簡單的呼叫Conncet函式,僅當連線完成時,無論成功還是失敗,該函式都返回,連線通知如何被處理是一個MFC內部的實現細節。
相關文章
- 【網路程式設計】socket詳解程式設計
- UDP&TCP Linux網路應用程式設計詳解UDPTCPLinux程式設計
- 網路程式設計TCP/IP詳解程式設計TCP
- java網路程式設計(TCP詳解)Java程式設計TCP
- Java 網路程式設計 —— Socket 詳解Java程式設計
- liunx程式設計之const使用詳解(轉)程式設計
- VC++深入詳解(12):網路程式設計C++程式設計
- python網路-Socket之TCP程式設計(26)PythonTCP程式設計
- Linux之19——Shell程式設計基礎詳解Linux程式設計
- Java 程式設計要點之 I/O 流詳解Java程式設計
- 計算機網路之IP地址、子網掩碼以及網路號之間的聯絡詳解計算機網路
- Java網路程式設計和NIO詳解3:IO模型與Java網路程式設計模型Java程式設計模型
- PHP Socket 程式設計詳解PHP程式設計
- Java 程式設計要點之併發(Concurrency)詳解Java程式設計
- 網路爬蟲詳細設計方案爬蟲
- 網路通訊程式設計程式設計
- 網路協程程式設計程式設計
- Socket 程式設計 (網路篇)程式設計
- py網路工具程式設計程式設計
- 網路協議之:memcached text protocol詳解協議Protocol
- 程式設計師之間的十八層鄙視網路程式設計師
- Java網路程式設計和NIO詳解9:基於NIO的網路程式設計框架NettyJava程式設計框架Netty
- 泛型程式設計詳解(一)泛型程式設計
- Flutter非同步程式設計詳解Flutter非同步程式設計
- MFC下CSocket程式設計詳解程式設計
- 網路程式設計之socket程式設計
- 設計模式之單例模式詳解設計模式單例
- Java設計模式之策略模式詳解Java設計模式
- 大資料之ETL設計詳解大資料
- JS非同步程式設計之Promise詳解和使用總結JS非同步程式設計Promise
- Java網路程式設計和NIO詳解6:Linux epoll實現原理詳解Java程式設計Linux
- 網路協議之:socket協議詳解之Datagram Socket協議
- shell程式設計-sed命令詳解(超詳細)程式設計
- 網路協議之:Domain name service DNS詳解協議AIDNS
- 網路協議之:memcached binary protocol詳解協議Protocol
- Matlab程式設計之——卷積神經網路CNN程式碼解析Matlab程式設計卷積神經網路CNN
- Python網路Socket程式設計Python程式設計
- Docker 網路模型之 macvlan 詳解,圖解,實驗完整Docker模型Mac圖解