天天看點

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事件,其通信流程如下。

MFC- 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 }      

轉載于:https://www.cnblogs.com/506941763lcj/p/11042313.html

繼續閱讀