MFC 一般 TCP 通信應用歷程

任何通信協議的格式都是由命令和附帶數據兩部分組成,包括網絡通信協議和硬件通信協議。命令號也叫協議號或協議編號,就是用一個數字或字符串通知對方執行一個對應的命令。大部分協議在發送命令號後還要發送附帶數據,例如,登錄協議要附帶賬號和密碼等數據。

創建工程名爲“s”和“c”的兩個對話框程序,演示 C/S 結構的網絡版信息管理軟件開發過程。客戶端通過 TCP 對服務器上的數據進行增、刪、改、查等功能。在應用程序嚮導的第 2 頁,選中“WIndows Sockets”複選框。如果沒有選擇該選項,那麼可以在 stdafx.h 中包含“afxsock.h”頭文件,並在 App類的 InitInstance 函數中調用 AfxSocketInit 函數。

服務器程序設計

1.在服務器“s”工程的 CSApp 類頭文件中,添加一個用於數據管理的鏈表變量。

//此處枚舉類型定義和結構體類型定義均爲全局定義。不屬於類的數據類型
enum
{
	//定義協議編號
	INF_ADD = 0x1234,
	INF_BROW,
	INF_DEL,
	INF_MOD,
	INF_FIND
};

struct SData
{
	int nNumb;
	char sName[20];
	float fSala;
};

#include<afxtempl.h>

class CSApp:public CWinApp
{
public:
    CList<SData, SData>m_list;
    ...

2.在“s”工程中創建兩個 CSSocket 類的派生類 CListenSocket 和 CClientSocket。

3.在偵聽類(CListenSocket)中添加虛函數 OnAccept,用於截獲客戶端的鏈接。

#include "ClientSocket.h"
void CListenSocket::OnAccept(int nErrorCode)
{
	//新建一個CClientSocket對象,與新連接進入的客戶端關聯
	CClientSocket* pSock = new CClientSocket;
	if (!Accept(*pSock))
		delete pSock;

	CSocket::OnAccept(nErrorCode);
}

4.在應答類(CClientSocket)中添加虛函數 OnClose,在客戶端連接斷開後及時清理堆空間。

void CClientSocket::OnClose(int nErrorCode)
{
	//清理對象所佔用的堆空間
	delete this;

	CSocket::OnClose(nErrorCode);
}

5.在應答類(CClientSocket)中添加虛函數 OnReceive,用於接收和解析客戶端協議。

void CClientSocket::OnReceive(int nErrorCode)
{
	int nCmd = 0, nLen = 0;
	if (Receive(&nCmd, sizeof(nCmd)) <= 0)
		return;

	switch ()
	{
	case INF_ADD:
		AddData();
		break;
	case INF_BROW:
		Browse();
		break;
	}

	CSocket::OnReceive(nErrorCode);
}

6.在CClientSocket 類中,添加兩個私有函數AddData 和 Browse 作爲協議處理函數。

extern CSApp theApp;
void CClientSocket::AddData()
{
	//添加數據的協議處理
	SData data;
	if (Receive(&data, sizeof(data) <= 0))
		return;
	theApp.m_list.AddTail(data);
}

void CClientSocket::Browse()
{
	//瀏覽數據協議處理,先發送總數再逐條發送具體數據
	int nCount = theApp.m_list.GetCount();
	Send(&nCount, sizeof(nCount));
	POSITION pos = theApp.m_list.GetHeadPosition();
	while (pos)
	{
		SData data = theApp.m_list.GetNext(pos);
		Send(&data, sizeof(data));
	}
}

7.在主對話框類的頭文件中,添加一個 CListenSocket 類型的成員變量。

#include "ListenSocket.h"
class CSDlg:public CDialog
{
    CListenSocket m_sock;
    ...

8.修改主對話框初始化函數的代碼。

BOLL CSDlg:OnInitDialog()
{
    CDialog::OnInitDialog();
    if(m_sock.Create(8668))
        m_sock.Listen();
    else
    {
        CString str;
        str.Format("Socket Create Error:%d", GetLastError());
        AfxMessageBox(str);
    }
}

9.編譯並運行,測試服務器端代碼。

客戶端代碼編寫

1.在客戶端“c”工程的主對話框中添加一些控件, 如下圖

2.修改控件屬性,給控件取 ID 值

3.通過類嚮導爲列表控件建立控件型關聯變量,CListCtrl m_list;

4.在主對話框的頭文件中,添加一個 CSocket 類型的成員變量,以及通信數據和協議定義。

enum
{
	//定義協議編號
	INF_ADD = 0x1234,
	INF_BROW,
	INF_DEL,
	INF_MOD,
	INF_FIND
};

struct SData
{
	int nNumb;
	char sName[20];
	float fSala;
};

class CCDlg:public CDialog
{
public:
    CSocket m_sock;
    ...
 

5.修改主對話框初始化函數的代碼。

BOOL CCDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    m_sock.Create();
    if(!m_sock.Connect("10.27.10.3", 8668))
    {
        AfxMessageBox(" 連接失敗:無法連接服務器,請檢查你的網絡連接!");
        EndDialog(IDCANCEL);
        return FALSE;
    }
    m_lisr.InsertColumn(0, "工號", 0, 100);
    m_lisr.InsertColumn(1, "姓名", 0, 100);
    m_lisr.InsertColumn(2, "工資", 0, 100);
    OnRefresh();
...

6.在主對話框中添加一個私有成員函數 OnFresh,用於從服務器接收數據來刷新列表。

void CCDlg::OnFresh()
{
    //發送完協議編號後,先接收數據總數在逐條接收每條具體數據
    int nCmd = INF_BROW;
    m_sock.Send(&nCmd, sizeof(nCmd));
    int nCount = 0, i = 0;
    m_sock.Receive(&nCount, sizeof(nCount));
    m_list.DeleteAllItems();
    SData Data;
    CString str;
    while(i < nCouunt)
    {
        //從服務器端接收每條數據,並將內容插入到列表控件中顯示
        m_sock.Receive(&data, sizeof(data));
        str.Format("%d", data.nNumb);
        m_list.InsertItem(i, str);
        m_list.SetItemText(i, 1, data.sName);
        str.Format("%0.2f", data.fSala);
        m_list.SetItemText(i, 2, str);
        ++i;
    }
}

7.建立“添加”按鈕的消息映射函數 OnAdd。

void CCDlg::OnAdd()
{
    //先發送協議編號,再發送一條信息數據到服務器端
    int nCmd = INF_ADD;
    m_sock.Send(&nCmd, sizeof(nCmd));
    SData data = {GetDlgItemInt(IDC_NUMB);}
    GetDlgItemText(IDC_NAME, data.sName, sizeof(data.sName));
    CSTring str;
    GetDlgItemText(IDC_SALA, str);
    data.fSala = (float)atof(str);
    m_sock.Send(&data, sizeof(data));
    OnRefresh();
}

//int GetDlgItemText( HWND hDlg , int nID, LPTSTR lpStr, int nMaxCount) const;
//int GetDlgItemText( int nID, CString& rString) const;
/*
UINT GetDlgItemInt( 
   int nID, 
   BOOL* lpTrans = NULL, 
   BOOL bSigned = TRUE  
) const;
int i = GetDlgItemInt(IDC_EDIT_TEMPERATURE, NULL, 1);  //若看做有符號數,則bSigned爲1,返回值直接以int類型去接收
UINT j = GetDlgItemInt(IDC_EDIT_TEMPERATURE, NULL, 0);  //若看做無符號數,則bSigned爲0,返回值按照UINT類型來接收
*/

8.編譯並運行,測試服務器端和客戶端代碼。

發佈了54 篇原創文章 · 獲贊 34 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章