MFC实现简单聊天客户端与服务端

May 19, 2015 19:47 · 2355 words · 5 minute read MFC

注意事项:

针对vs2013 - 由于mfc在vs2013支持并不完整,根据MSDN请前往微软开发者中心下载 Multibyte MFC Library for Visual Studio 2013,vs2015同理 - 务必将项目属性中的 字符集 更改为 使用多字节字符集 ,否则无法编译通过 - 本代码为课本代码,课本上源码有坑,注意对比

客户端服务端效果显示

客户端的实现

ChatClientDlg对话框具体实现

	//需要增加判断是否连接
	// ChatClientDlg.cpp : 实现文件
	//

	#include "stdafx.h"
	#include "ChatClient.h"
	#include "ChatClientDlg.h"
	#include "afxdialogex.h"

	#ifdef _DEBUG
	#define new DEBUG_NEW
	#endif


	// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

	class CAboutDlg : public CDialogEx
	{
	public:
		CAboutDlg();

	// 对话框数据
		enum { IDD = IDD_ABOUTBOX };

		protected:
		virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	// 实现
	protected:
		DECLARE_MESSAGE_MAP()
	};

	CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
	{
	}

	void CAboutDlg::DoDataExchange(CDataExchange* pDX)
	{
		CDialogEx::DoDataExchange(pDX);
	}

	BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
	END_MESSAGE_MAP()


	// CChatClientDlg 对话框



	CChatClientDlg::CChatClientDlg(CWnd* pParent /*=NULL*/)
		: CDialogEx(CChatClientDlg::IDD, pParent)
	{
		m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
		m_Port = 0;
		m_IPAddress = _T("");
	}

	void CChatClientDlg::DoDataExchange(CDataExchange* pDX)
	{
		CDialogEx::DoDataExchange(pDX);
		DDX_Control(pDX, IDC_HISTORY, m_list);
		DDX_Control(pDX, IDC_INPUT, m_input);
	}

	BEGIN_MESSAGE_MAP(CChatClientDlg, CDialogEx)
		ON_WM_SYSCOMMAND()
		ON_WM_PAINT()
		ON_WM_QUERYDRAGICON()
		ON_BN_CLICKED(IDC_START, &CChatClientDlg::OnBnClickedStart)
		ON_BN_CLICKED(IDC_DISCONNECT, &CChatClientDlg::OnBnClickedDisconnect)
		ON_BN_CLICKED(IDC_SEND, &CChatClientDlg::OnBnClickedSend)
	END_MESSAGE_MAP()


	// CChatClientDlg 消息处理程序

	BOOL CChatClientDlg::OnInitDialog()
	{
		CDialogEx::OnInitDialog();
		//设定按钮控件的操作顺序
		// _T(字符串)实现多字符集
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
		GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
		GetDlgItem(IDC_INPUT)->EnableWindow(FALSE);
		GetDlgItem(IDC_IPADDRESS)->SetWindowText(_T("127.0.0.1"));
		GetDlgItem(IDC_PORT)->SetWindowText(_T("5000"));

		// 将“关于...”菜单项添加到系统菜单中。

		// IDM_ABOUTBOX 必须在系统命令范围内。
		ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
		ASSERT(IDM_ABOUTBOX < 0xF000);

		CMenu* pSysMenu = GetSystemMenu(FALSE);
		if (pSysMenu != NULL)
		{
			BOOL bNameValid;
			CString strAboutMenu;
			bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
			ASSERT(bNameValid);
			if (!strAboutMenu.IsEmpty())
			{
				pSysMenu->AppendMenu(MF_SEPARATOR);
				pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
			}
		}

		// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
		//  执行此操作
		SetIcon(m_hIcon, TRUE);			// 设置大图标
		SetIcon(m_hIcon, FALSE);		// 设置小图标

		ShowWindow(SW_MAXIMIZE);

		// TODO:  在此添加额外的初始化代码

		return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
	}

	void CChatClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
	{
		if ((nID & 0xFFF0) == IDM_ABOUTBOX)
		{
			CAboutDlg dlgAbout;
			dlgAbout.DoModal();
		}
		else
		{
			CDialogEx::OnSysCommand(nID, lParam);
		}
	}

	// 如果向对话框添加最小化按钮,则需要下面的代码
	//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
	//  这将由框架自动完成。

	void CChatClientDlg::OnPaint()
	{
		if (IsIconic())
		{
			CPaintDC dc(this); // 用于绘制的设备上下文

			SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

			// 使图标在工作区矩形中居中
			int cxIcon = GetSystemMetrics(SM_CXICON);
			int cyIcon = GetSystemMetrics(SM_CYICON);
			CRect rect;
			GetClientRect(&rect);
			int x = (rect.Width() - cxIcon + 1) / 2;
			int y = (rect.Height() - cyIcon + 1) / 2;

			// 绘制图标
			dc.DrawIcon(x, y, m_hIcon);
		}
		else
		{
			CDialogEx::OnPaint();
		}
	}

	//当用户拖动最小化窗口时系统调用此函数取得光标
	//显示。
	HCURSOR CChatClientDlg::OnQueryDragIcon()
	{
		return static_cast<HCURSOR>(m_hIcon);
	}


	//创建对象后调用Connect()函数与服务器连接
	void CChatClientDlg::OnBnClickedStart()
	{
		// TODO:  在此添加控件通知处理程序代码
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
		GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
		GetDlgItem(IDC_START)->EnableWindow(FALSE);
		CString temp1, temp2;
		GetDlgItem(IDC_IPADDRESS)->GetWindowText(temp1);
		m_IPAddress = temp1.GetBuffer(temp1.GetLength());
		GetDlgItem(IDC_PORT)->GetWindowText(temp2);
		m_Port = atoi(temp2);  //强制类型转换可能乱码
		m_client = new CMySocket;
		m_
		if ((*m_client).Create() == 0)  //错误处理?
		{
			CString errcode;
			errcode.Format(_T("%d"), GetLastError());
			m_list.AddString(errcode);
			UpdateData(FALSE);
		}
		(*m_client).Connect(m_IPAddress, m_Port);  //连接
		//更新控件状态:连接成功时IP和端口不可输入,输入窗口可用
		GetDlgItem(IDC_IPADDRESS)->EnableWindow(FALSE);
		GetDlgItem(IDC_PORT)->EnableWindow(FALSE);
		GetDlgItem(IDC_INPUT)->EnableWindow(TRUE);
	}

	//关闭套接字,删除指针释放内存空间,在控件中显示当前状态
	void CChatClientDlg::OnBnClickedDisconnect()
	{
		// TODO:  在此添加控件通知处理程序代码
		(*m_client).Close();
		delete m_client;
		//更新控件状态
		GetDlgItem(IDC_IPADDRESS)->EnableWindow(TRUE);
		GetDlgItem(IDC_PORT)->EnableWindow(TRUE);
		GetDlgItem(IDC_INPUT)->EnableWindow(TRUE);
		GetDlgItem(IDC_START)->EnableWindow(TRUE);
		GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
		//
		CString str;
		m_tm = CTime::GetCurrentTime();
		str = m_tm.Format("%X");
		str += "从服务器断开";
		m_list.AddString(str);
		UpdateData(FALSE);
	}

	//读入控件内容,存储在buff,调用send()函数发送,最后在控件中显示发送的内容
	void CChatClientDlg::OnBnClickedSend()
	{
		//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
		//需要增加判断是否连接

		m_input.GetWindowText(buff, 200);
		CString temp1 = "我:";
		CString temp2 = buff;
		CString formatbuff = "#c#s#" + (CString)buff;
		(*m_client).Send(formatbuff, 200, 0);
		CString str;
		m_tm = CTime::GetCurrentTime();
		str = m_tm.Format("%X");
		temp1 = str + temp1;
		temp1 += temp2;
		m_list.AddString(temp1);
		UpdateData(FALSE);
		m_input.SetWindowText(""); //输入控件清空
	}

重载Socket部分函数

	// MySocket.cpp : 实现文件
	//

	#include "stdafx.h"
	#include "ChatClient.h"
	#include "MySocket.h"
	#include "ChatClientDlg.h"

	// CMySocket

	CMySocket::CMySocket()
	{
	}

	CMySocket::~CMySocket()
	{
	}


	// CMySocket 成员函数
	//重载OnReceive函数,用于接收数据
	void CMySocket::OnReceive(int nErrorCode)
	{
		CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();
		Receive(dlg->buff, 200, 0);
		CString temp1 = "server: ";
		CString temp2 = dlg->buff;
		CString str;
		dlg->m_tm = CTime::GetCurrentTime();
		str = dlg->m_tm.Format("%X");
		temp1 = str + temp1;
		temp1 += temp2;
		dlg->m_list.AddString(temp1);
		dlg->m_list.UpdateData(FALSE);
		CAsyncSocket::OnReceive(nErrorCode);
	}

	//重载OnConnec函数用于连接
	void CMySocket::OnConnect(int nErrorCode)
	{
		CChatClientDlg* dlg = (CChatClientDlg*)AfxGetApp()->GetMainWnd();
		CString str;
		dlg->m_tm = CTime::GetCurrentTime();
		str = dlg->m_tm.Format("%X");
		str += "与服务器连接成功";
		dlg->m_list.AddString(str);
		CAsyncSocket::OnConnect(nErrorCode);
	}

服务端的实现

ChatClientDlg对话框具体实现

	// ChatServerDlg.cpp : 实现文件
	//

	#include "stdafx.h"
	#include "ChatServer.h"
	#include "ChatServerDlg.h"
	#include "afxdialogex.h"

	#ifdef _DEBUG
	#define new DEBUG_NEW
	#endif


	// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

	class CAboutDlg : public CDialogEx
	{
	public:
		CAboutDlg();

	// 对话框数据
		enum { IDD = IDD_ABOUTBOX };

		protected:
		virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	// 实现
	protected:
		DECLARE_MESSAGE_MAP()
	};

	CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
	{
	}

	void CAboutDlg::DoDataExchange(CDataExchange* pDX)
	{
		CDialogEx::DoDataExchange(pDX);
	}

	BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
	END_MESSAGE_MAP()


	// CChatServerDlg 对话框



	CChatServerDlg::CChatServerDlg(CWnd* pParent /*=NULL*/)
		: CDialogEx(CChatServerDlg::IDD, pParent)
	{
		m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
		m_port = 0;
	}

	void CChatServerDlg::DoDataExchange(CDataExchange* pDX)
	{
		CDialogEx::DoDataExchange(pDX);
		DDX_Control(pDX, IDC_HISTORY, m_list);
		DDX_Control(pDX, IDC_INPUT, m_input);
	}

	BEGIN_MESSAGE_MAP(CChatServerDlg, CDialogEx)
		ON_WM_SYSCOMMAND()
		ON_WM_PAINT()
		ON_WM_QUERYDRAGICON()
		ON_BN_CLICKED(IDC_START, &CChatServerDlg::OnBnClickedStart)
		ON_BN_CLICKED(IDC_DISCONNECT, &CChatServerDlg::OnBnClickedDisconnect)
		ON_BN_CLICKED(IDC_SEND, &CChatServerDlg::OnBnClickedSend)
	END_MESSAGE_MAP()


	// CChatServerDlg 消息处理程序

	BOOL CChatServerDlg::OnInitDialog()
	{
		CDialogEx::OnInitDialog();
		//防止误操作
		GetDlgItem(IDC_PORT)->SetWindowText("5000");
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
		GetDlgItem(IDC_INPUT)->EnableWindow(FALSE);
		GetDlgItem(IDC_SEND)->EnableWindow(FALSE);

		// 将“关于...”菜单项添加到系统菜单中。

		// IDM_ABOUTBOX 必须在系统命令范围内。
		ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
		ASSERT(IDM_ABOUTBOX < 0xF000);

		CMenu* pSysMenu = GetSystemMenu(FALSE);
		if (pSysMenu != NULL)
		{
			BOOL bNameValid;
			CString strAboutMenu;
			bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
			ASSERT(bNameValid);
			if (!strAboutMenu.IsEmpty())
			{
				pSysMenu->AppendMenu(MF_SEPARATOR);
				pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
			}
		}

		// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
		//  执行此操作
		SetIcon(m_hIcon, TRUE);			// 设置大图标
		SetIcon(m_hIcon, FALSE);		// 设置小图标

		// TODO:  在此添加额外的初始化代码

		return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
	}

	void CChatServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
	{
		if ((nID & 0xFFF0) == IDM_ABOUTBOX)
		{
			CAboutDlg dlgAbout;
			dlgAbout.DoModal();
		}
		else
		{
			CDialogEx::OnSysCommand(nID, lParam);
		}
	}

	// 如果向对话框添加最小化按钮,则需要下面的代码
	//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
	//  这将由框架自动完成。

	void CChatServerDlg::OnPaint()
	{
		if (IsIconic())
		{
			CPaintDC dc(this); // 用于绘制的设备上下文

			SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

			// 使图标在工作区矩形中居中
			int cxIcon = GetSystemMetrics(SM_CXICON);
			int cyIcon = GetSystemMetrics(SM_CYICON);
			CRect rect;
			GetClientRect(&rect);
			int x = (rect.Width() - cxIcon + 1) / 2;
			int y = (rect.Height() - cyIcon + 1) / 2;

			// 绘制图标
			dc.DrawIcon(x, y, m_hIcon);
		}
		else
		{
			CDialogEx::OnPaint();
		}
	}

	//当用户拖动最小化窗口时系统调用此函数取得光标
	//显示。
	HCURSOR CChatServerDlg::OnQueryDragIcon()
	{
		return static_cast<HCURSOR>(m_hIcon);
	}

	//调用create创建监听套接字,调用listen监听,显示相关信息并更新控件状态
	void CChatServerDlg::OnBnClickedStart()
	{
		// TODO:  在此添加控件通知处理程序代码
		//GetDlgItem(IDC_START)->EnableWindow(FALSE);
		CString temp;
		GetDlgItem(IDC_PORT)->GetWindowText(temp); //获取端口号
		UINT port = atoi(temp.GetBuffer());
		//初始化
		m_server = new CServerSocket;
		m_client = new CClientSocket;
		if ((*m_server).Create(port) == 0)//错误时显示错误code
		{
			static int code = (*m_server).GetLastError();
			CString err;
			err.Format("%/d", code);
			CString error = "Create Error code = ";
			error += err;
			MessageBox(error);
			return;
		}
		//监听
		if ((*m_server).Listen() == 0)
		{
			static int code = (*m_server).GetLastError();
			CString err;
			err.Format("%d", code);
			CString error = "Create Error Code = ";
			error += err;
			MessageBox(error);
			return;
		}
		//更新控件状态
		GetDlgItem(IDC_START)->EnableWindow(FALSE);
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(TRUE);
		GetDlgItem(IDC_INPUT)->EnableWindow(TRUE);
		GetDlgItem(IDC_SEND)->EnableWindow(TRUE);
		CString str;
		m_tm = CTime::GetCurrentTime();
		str = m_tm.Format("%X");
		str += "建立服务";
		m_list.AddString(str);
		UpdateData(FALSE);
	}

	//
	void CChatServerDlg::OnBnClickedDisconnect()
	{
		// TODO:  在此添加控件通知处理程序代码
		(*m_server).Close();
		delete m_server;
		delete m_client;
		CString str;
		m_tm = CTime::GetCurrentTime();
		str = m_tm.Format("%X");
		str += "服务中断";
		m_list.AddString(str);
		UpdateData(FALSE);
		//更新控件状态
		GetDlgItem(IDC_START)->EnableWindow(TRUE);
		GetDlgItem(IDC_DISCONNECT)->EnableWindow(FALSE);
		GetDlgItem(IDC_INPUT)->EnableWindow(FALSE);
		GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
	}

	//
	void CChatServerDlg::OnBnClickedSend()
	{
		// TODO:  在此添加控件通知处理程序代码
		m_input.GetWindowText(buff, 200);
		(*m_client).Send(buff, 200);
		CString temp1 = "我:";
		CString temp2 = buff;
		CString str;
		m_tm = CTime::GetCurrentTime();
		str = m_tm.Format("%X");
		temp1 = str + temp1;
		temp1 += temp2;
		CChatServerDlg* dlg = (CChatServerDlg*)AfxGetApp()->GetMainWnd();
		m_list.AddString(temp1);
		UpdateData(FALSE);
		m_input.SetWindowText("");
	}

ClientSocket重载函数

	// ClientSocket.cpp : 实现文件
	//

	#include "stdafx.h"
	#include "ChatServer.h"
	#include "ClientSocket.h"
	#include "ChatServerDlg.h"

	// CClientSocket

	CClientSocket::CClientSocket()
	{
	}

	CClientSocket::~CClientSocket()
	{
	}


	// CClientSocket 成员函数
	void CClientSocket::OnReceive(int nErrorCode)
	{
		CChatServerDlg* dlg = (CChatServerDlg*)AfxGetApp()->GetMainWnd();
		//获取时间
		dlg->m_tm = CTime::GetCurrentTime();
		CString str;
		str = dlg->m_tm.Format("%X");
		//接收数据
		Receive(dlg->buff, 200, 0);
		CString temp1 = dlg->buff;
		//解码收到的数据
		int index = 0;
		for (int i=0; i < 3; i++)
		{
			index = temp1.Find('#', index);
			index++;
		}
		int sum = temp1.GetLength();
		int count = sum - index;
		CString message = temp1.Mid(index, count);
		CString temp2 = "客户端:";
		temp2 = str + temp2;
		temp2 += message;
		dlg->m_list.AddString(temp2);
		CAsyncSocket::OnReceive(nErrorCode);
	}

##ServerSocket重载OnAccept

	// ServerSocket.cpp : 实现文件
	//

	#include "stdafx.h"
	#include "ChatServer.h"
	#include "ServerSocket.h"
	#include "ChatServerDlg.h"

	// CServerSocket

	CServerSocket::CServerSocket()
	{
	}

	CServerSocket::~CServerSocket()
	{
	}


	// CServerSocket 成员函数
	void CServerSocket::OnAccept(int nErrorCode)
	{
		CChatServerDlg* dlg = (CChatServerDlg*)AfxGetApp()->GetMainWnd();
		dlg->m_client = new CClientSocket;
		Accept(*(dlg->m_client));
		CString str;
		dlg->m_tm = CTime::GetCurrentTime();
		str = dlg->m_tm.Format("%X");
		str += "客户端连接成功";
		dlg->m_list.UpdateData(FALSE);
		CAsyncSocket::OnAccept(nErrorCode);
	}