MFC- socket 編程


一、CAsyncSocket類

CAsyncSocket屬於異步非阻塞類。

CAsyncSocket類采用了windows socket中的WSAAsyncSelect模型。CAsyncSocket 類是在很低的層次上對windows socket API進行了封裝,它的成員函數和winsock API的函數調用直接對應,一個CAsyncSocket對象代表了一個windows套接字,它是網絡通信的端點。該類將根據不同的windows套接字消息調CAsyncSocket類的回調函數。

OnAccept 

通知偵聽套接字,它可以通過調用Accept,接受掛起連接請求

 

OnClose 

通知套接字,關閉對它的套接字連接

 

OnConnect 

通知連接套接字,連接嘗試已經完成,無論成功或失敗

 

OnOutOfBandData 

通知接收套接字,在套接字上有帶外數據讀入,通常是忙消息

 

OnReceive 

通知偵聽套接字,通過調用Receive恢復數據

 

OnSend 

通知套接字,通過調用Send,它可以發送數據

 

網絡應用程序一般采用客戶端/服務器模式,他們使用的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來刪除釋放該對象。

CAsyncSocket編程注意問題:

a) 阻塞處理。CAsyncSocket對象專用於異步操作,不支持阻塞工作模式,如果應用程序需要支持阻塞操作,必須自己解決。

b) 字節順序的轉換。在不同的結構類型的計算機之間進行數據傳輸時,可能會有計算機之間字節存儲順序不一致的情況,需要自己對不用的字節順序進行轉換。

c) 字符串轉換。同樣不同結構類型的計算機的字符串順序也可能不同,需要自行轉換。

d)在使用CAsyncSocket之前,必須調用AfxSocketInit初始化WinSock環境,而AfxSocketInit會創建一個隱藏的CSocketWnd對象,由於這個對象由Cwnd派生,因此它能夠接收Windows消息。一方面它會接受各個CAsyncSocket的狀態報告,另一方面它能捕捉系統發出的各種SOCKET事件,其通信流程如下。

二、CSocket類

CSocket是MFC在CAsyncSocket基礎上派生的一個同步阻塞Socket的封裝類。

CSocket類是從CAsyncsocket派生而來的,它繼承了CAsyncsocket對WindowsSockets API的封裝。與CAsyncsocket對象相比,CSocket對象代表了WindowsSockets API的更高一級的抽象化。

a)在使用MFC編寫socket程序時,必須要包含<afxsock.h>都文件。

b) AfxSocketInit() 這個函數,在使用CSocket前一定要先調用該函數,否則使用CSocket會出錯;並且該函數還有一個重要的使用方式,就是在某個線程下使用 CSocket 前一定要調用,就算主線程調用了該函數,在子線程下使用 CSocket 也要先調用該函數,要不會出錯。

c) 還要注意的是, Create 方法已經包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再調用 Bind ,要不一定出錯。

三、代碼實現

設計兩個對話框應用程序,通過TCP/IP進行通信,使用MFC的CSocket類實現服務器端和客戶端之間的相互通信。

3.1服務器端

服務器端的socket通信需要用到兩個socket:

  1. 用來監聽連接的socket
  2. 用來和客戶端通信的socket,此socket中保存了客戶端信息

因此服務器端需要添加兩個繼承自CSocket的類,分別起名為CServerSocket、CConnectSocket。

3.1.1 CServerSocket 

  此socket主要用來監聽客戶端請求,當有請求到來時,MFC框架將調用OnAccept函數,所以我們需要重寫CSocket類的OnAccept函數。

 1 /////////////////////////////////////////////////////////
 2 # ServerSocket.h 文件
 3 /////////////////////////////////////////////////////////
 4 class CServerSocket : public CSocket
 5 {
 6 public:
 7     CServerSocket();
 8     virtual ~CServerSocket();
 9 
10     void OnAccept(int nErrorCode);
11     //開啟socket服務
12     void StartServer(UINT nPort);
13     //發送消息函數
14     void MessageSend(const char* pMesg);
15 
16 private:
17     //保存的是客戶端的socket信息
18     CConnectSocket m_clientSock;
19 };

  在OnAccept函數中調用Accept函數,接受客戶端請求,另外將客戶信息保存到m_clientSock中,使用此套接字對象與客戶端進行通信,發送信息可以直接調用API函數Send。

  在StartServer函數中做了兩件事兒,創建套接字,監聽套接字。需要主要的是Create函數內部已經對套接字進行了綁定,所以不需要再次綁定。

 1 /////////////////////////////////////////////////////////
 2 # ServerSocket.cpp 文件
 3 /////////////////////////////////////////////////////////
 4 void CServerSocket::OnAccept(int nErrorCode)
 5 {
 6     Accept(m_clientSock);
 7     CSocket::OnAccept(nErrorCode);
 8 }
 9 
10 void CServerSocket::StartServer(UINT nPort)
11 {
12     if (!Create(nPort))
13     {
14         AfxMessageBox(_T("Socket 創建失敗!"));
15         return;
16     }
17     if (!Listen(5))
18     {
19         AfxMessageBox(_T("Socket 監聽失敗!"));
20         return;
21     }
22 }
23 
24 void CServerSocket::MessageSend(const char* pMesg)
25 {
26     m_clientSock.Send(pMesg, strlen(pMesg) + 1);
27 }

3.1.2CConnectSocket類

  此類中保存了客戶端信息,所以用來與客戶端進行通信,重寫了OnSend和OnReceive函數,這兩個函數由MFC框架調用。

 1 /////////////////////////////////////////////////////////
 2 # ConnectSocket.h 文件
 3 /////////////////////////////////////////////////////////
 4 class CConnectSocket : public CSocket
 5 {
 6 public:
 7     CConnectSocket();
 8     virtual ~CConnectSocket();
 9 
10     //函數重寫
11     void OnSend(int nErrorCode);
12     void OnReceive(int nErrorCode);
13 };

當客戶端與客戶端連接成功之后服務器端框架會調用OnSend函數給客戶端發送通知。另外,當服務器端接收到客戶端發來的消息之后,MFC框架會調用OnReceive函數,我們可以在此函數中對接收到的消息進行處理。

 1 /////////////////////////////////////////////////////////
 2 # ConnectSocket.cpp 文件
 3 /////////////////////////////////////////////////////////
 4 void CConnectSocket::OnSend(int nErrorCode)
 5 {
 6     char* pSend = "你好, 我是服務器,我們已經成功建立了連接!";
 7     Send(pSend, strlen(pSend) + 1);
 8     CSocket::OnSend(nErrorCode);
 9 }
10 
11 void CConnectSocket::OnReceive(int nErrorCode)
12 {
13     char bufRecv[1024];
14     int nCount = Receive(bufRecv, 1024);
15     bufRecv[nCount] = 0;
16     CUnicodeAndChar uc;
17     CString str = uc.MultiToWide(string(bufRecv));
18     AfxMessageBox(str);
19     CSocket::OnReceive(nErrorCode);
20 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM