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);
    }