MFC课程设计报告

河海大学计算机及信息工程学院(常州)

MFC课程设计报告

题    目   聊天室程序设计   

学    号    20062325       

专     业   计算机科学与技术

授课班号    243002         

学生姓名    邓燕           

指导教师    景雪琴老师     

完成时间    2008.12.30      

题目:设计一个聊天室系统,实现多人一起聊天。

一.     引言

1.1开发背景

随着网络信息时代的来临,INTERNET应用越来越广泛,人们越来越习惯于在网上获取和交流信息。据调查显示,80%以上的人上网都会打开聊天工具来聊天,而几乎每一个年轻人都会去聊天。使用网上聊天已经成为现代年轻人一种新的交往方式。聊天室更适合于陌生人之间进行较为主观、感兴化的讨论。所以有大部分的人会进入聊天室聊天,它会给人一个完全自由的聊天世界。因此我联系所学的MFC知识,决定做一个简易的聊天室程序。

1.2开发环境以及运行环境

1.2.1 开发环境

l   Intel® Pentium® 4 2.0GHz,512M内存,80G硬盘

l   Microsoft® Windows? XP Professional

l   Microsoft® Visual C++ 6.0

1.2.2 运行环境

l   Intel® Pentium® 2及以上处理器,32M以上内存,4G以上硬盘

l   Microsoft® Windows? XP操作系统

l   800*600或以上的屏幕分辨率

二.需求分析

1、 用WINSOCK实现简单的网络聊天;

2、 在MFC开发平台上编写一个聊天系统;

3、 得server和client之间可以相互通信;

4、多人能够发送信息至服务器,首先显示进入聊天室的成员名,然后显示对方名字和聊天信息,可以并发执行。

三、算法分析

   建立连接过程流程图如下:

   

四、详细设计

程序个模块代码如下:

   1.客户端

  (1)通过AppWizard生成基于对话框的应用程序ChatClient,在向导的第二步选择WindowsSockets的支持,其余步骤均用默认值。

  (2)增加一个登录服务器的对话框资源,其ID为IDD_SETUP,然后通过ClassWizard生成对应的基于CDialog的类CSetupDlg。

 (3)给对话框CSetupDlg增加3个编辑框,用来输入登录信息(聊天代号、服务器地址以及服务器端口号),

 (4)通过ClassWizard生成对应的基于CSocket的类CChatSocket。

 (5)给CChatSocket类声明一个主对话框CChatClientDlg指针类型的私有成员变量,其代码如下:

       protected:

        CChatClientDlg* m_pDlg;

并且在ChatSocket类的头文件开始处增加如下代码:

class CChatClientDlg;  //主对话框类

 (6)给ChatSocket类重载一个构造函数,其定义如下:

    CChatSocket::CChatSocket(CChatClientDlg* pDlg)

{

       m_pDlg = pDlg;

}

并且在ChatSocket类的CPP文件开始处增加如下语句:

#include "ChatClientDlg.h"

  (7)通过ClassWizard响应类的ChatSocket的OnReceive函数,表示可以接收数据了,其代码如下:

//通知客户端可以接受数据

void CChatSocket::OnReceive(int nErrorCode) {

           CSocket::OnReceive(nErrorCode);

          if(m_pDlg){

             PlaySound("F:\msg.wav",NULL,SND_ASYNC|SND_NODEFAULT);

              m_pDlg->ProcessPendingRead();

          }      

}

  (8)在主对话框ChatClientDlg的头文件中定义ChatSocket指针类型的私有成员变量,其代码如下:

CChatSocket* m_pSocket;

并且在CChatClientDlg类的头文件开始处增加如下代码:

class CChatSocket;//基于CSocket的新类CChatSocket

  (9)给对话框CChatClientDlg增加处理接收数据的共有成员函数ProcessPendingRead,其定义如下:

void CChatClientDlg::ProcessPendingRead()//处理接收的数据{

            //定义缓冲区

            char buffer[BUFFER_SIZE];

            //接收数据

            int nReceived = m_pSocket->Receive(buffer,BUFFER_SIZE,0);

            buffer[nReceived] = 0; 

           //将数据在列表框中显示出来

           CString str;

           str.Format("%s",buffer);

           m_ctrlMsgs.AddString(str);

}

  (10)给对话框CChatClientDlg增加发送数据的私有成员函数SendMsg:

//发送数据到服务器

void CChatClientDlg::SendMsg(CString strMsg){

             CString str;

             str.Format("%s:%s",m_strClientName,strMsg);

             m_pSocket->Send(str.GetBuffer(0),str.GetLength(),0);

}

并且在CChatClientDlg类的头文件开始处定义缓冲区的大小,其代码如下:

#define   BUFFER_SIZE   200

  (11)给对话框CChatClientDlg增加表示聊天代号的CString类型私有成员变量m_strClientName。

  (12)在对话框CChatClientDlg的OnInitialUpdate函数中增加登录服务器的代码:

BOOL CChatClientDlg::OnInitDialog()//登录服务器{

            CDialog::OnInitDialog();

            ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

            ASSERT(IDM_ABOUTBOX < 0xF000);

            CMenu* pSysMenu = GetSystemMenu(FALSE);

            if (pSysMenu != NULL)           {

                CString strAboutMenu;

                strAboutMenu.LoadString(IDS_ABOUTBOX);

                if (!strAboutMenu.IsEmpty())              {

                     pSysMenu->AppendMenu(MF_SEPARATOR);                     pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX, strAboutMenu);

                 }

            }

     SetIcon(m_hIcon, TRUE);        // Set big icon

            SetIcon(m_hIcon, FALSE);       // Set small icon

//显示登录对话框

CSetupDlg dlg;

           if(dlg.DoModal()==IDOK)         {

               //创建一个新的Socket

               m_pSocket = new CChatSocket(this);      

               if (!m_pSocket->Create())            {

                 delete m_pSocket;

                 m_pSocket = NULL;

                 AfxMessageBox("create socket failed");

                 return FALSE;

             }      

            //连接服务器

     while (!m_pSocket->Connect(dlg.m_strServer,dlg.m_nPort + 700))   {

if (AfxMessageBox("Failed to connect to server\nTry again?",MB_YESNO) == IDNO)             {

                  delete m_pSocket;

                  m_pSocket = NULL;

                  return FALSE;

               }

           }

           m_strClientName = dlg.m_strName;

        }

        else

           return FALSE;

        //发送登录消息给服务器

        SendMsg("进入聊天室"); 

       return TRUE;  // return TRUE  unless you set the focus to a control

}

  (13)给主对话框CChatClientDlg增加一个CListBox类型的变量m_strMsg。

  (14)给住对话框CChatClientDlg增加一个输入发送信息的编辑框,并生成对应的CString类型的变量m_strMsg。

(15)给主对话框CChatClientDlg增加一个【send】按钮,其响应函数定义如下:

void CChatClientDlg::OnSend() //响应控件send{

            UpdateData(TRUE);

  SendMsg(m_strMsg);

  m_strMsg.Empty();////清空编辑区

            UpdateData(FALSE);

}

  (16)给主对话框增添一个【离开】按钮,其响应函数定义如下:

void CChatClientDlg::leave()//离开{

           SendMsg("离开聊天室");

            m_ctrlMsgs.AddString("离开状态");

           m_pSocket->Close();

}

(17)给主对话框增添一个【连接】按钮,其响应函数如下:

void CChatClientDlg::land()//连接{

             OnInitDialog();

}

2.服务器端

  (1)通过AppWizard生成基于对话框的应用程序ChatServer,在向导的第二步选择WindowsSockets的支持,其余步骤均用默认值。

 (2)增加一个登录服务器的对话框资源,其ID为IDD_INT,然后通过ClassWizard生成对应的基于CDialog的类CInitDlg。

 (3)给对话框CInitDlg增加1个输入端口号的编辑框,并生成对应的整形变量m_nPort。

 (4)通过ClassWizard生成对应的基于CSocket的类CListeningSocket,用来监听服务器。

 (5)给CListeningSocket类声明一个主对话框CChatServerDlg指针类型的私有成员变量,其代码如下:

       protected:

        CChatServerDlg* m_pDlg;

并且在CListeningSocket类的头文件开始处增加如下代码:

class CChatServerDlg;  //主对话框类

 (6)给ChatSocket类重载一个构造函数,其定义如下:

    CChatSocket::CClientSocket(CChatServerDlg* pDlg)

{     m_pDlg = pDlg;  }

并且在CListeningSocket类的CPP文件开始处增加如下语句:

#include "ChatServerDlg.h"

  (7)通过ClassWizard响应类的CListeningSocket的OnAccept函数,表示客户端连接,其代码如下:

void CListeningSocket::OnAccept(int nErrorCode)

//通知服务器,有客户端需要连接服务器

{  CSocket::OnAccept(nErrorCode);

          //主对话框处理连接信息

          if(m_pDlg)

          m_pDlg->ProcessPendingAccept();

}

  (8)通过ClassWizard生成基于CSocket的新类CClientSocket,用来与客户端通信。

(9)给CClientSocket类声明一个主对话框CChatServerDlg指针类型的私有成员变量,其代码如下:

protected:

     CChatServerDlg* m_pDlg;

并且在CClientSocket类的头文件开始处增加如下语句:

class CChatServerDlg;

(10)给CClientSocket类重载一个构造函数,其定义如下:

CClientSocket::CClientSocket(CChatServerDlg* pDlg){

m_pDlg = pDlg;

}

并且在CClientSocket类的CPP文件开始处增加如下语句:

#include "ChatServerDlg.h"

(11)通过ClassWizard响应类的CClientSocket的OnReceive函数,表示数据已到达,其代码如下:

void CClientSocket::OnReceive(int nErrorCode)

 //通知服务器可以接受数据

{   CSocket::OnReceive(nErrorCode);

   if(m_pDlg)

         {  //让主对话框处理数据

            m_pDlg->ProcessPendingRead(this);

         }

}

(12)在主对话框CChatServerDlg的头文件那个增加两个私有成员变量,其代码如下:

 CListeningSocket* m_pSocket;      //监听套节字

      CPtrList m_connectionList;         //客户端套节字链表

并且在CChatSvrDlg的头文件开始处增加如下代码:

class CListeningSocket;

class CClientSocket;

(13)给主对话框CChatSvrDlg增加处理客户端连接信息的私有成员变量ProcessPendingAccept,其定义如下:

void CChatServerDlg::ProcessPendingAccept() {

           //生成一个新的套节字与客户端通讯

           CClientSocket* pSocket = new CClientSocket(this);

  if (m_pSocket->Accept(*pSocket))          {

               //将该套节字保存起来

               m_connectionList.AddTail(pSocket);

           }

           else

              delete pSocket;

}

(14)给主对话框CChatSvrDlg增加更新所有客户端的私有成员函数UpdateClient,其定义如下:

void CChatServerDlg::UpdateClients(char* buffer,int nBufferSize)

{

         for(POSITION pos = m_connectionList.GetHeadPosition();

 pos != NULL;)

        {

            //将新信息发送给每个客户端

CClientSocket* pSocket= (CClientSocket*)m_connectionList.GetNext(pos);

     if (buffer != NULL)

         pSocket->Send(buffer,nBufferSize,0);

      }

}

(15)给主对话框CChatSvrDlg增加接受数据的私有成ProcessPendingRe,

其定义如下:

void CChatServerDlg::ProcessPendingRead(CClientSocket* pSocket)

{

          //定义缓冲区

          char buffer[BUFFER_SIZE];

         //接收数据

         int nReceived = pSocket->Receive(buffer,BUFFER_SIZE,0);

         buffer[nReceived] = 0;

        //将数据在列表框中显示出来

          m_ctrlMsgs.AddString(buffer);

//将数据发送给每个客户

        UpdateClients(buffer,nReceived);

}

并且在CChatSvrDlg类的头文件开始处定义缓冲区的大小,其代码如下:

#define BUFFER_SIZE       200

(16)给主对话框CChatSvrDlg的OnInitialUpdate函数中显示初始化对话框,其定义如下:

BOOL CChatServerDlg::OnInitDialog()

{

        CDialog::OnInitDialog();

      ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

        ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);

        if (pSysMenu != NULL)

        {

            CString strAboutMenu;

            strAboutMenu.LoadString(IDS_ABOUTBOX);

            if (!strAboutMenu.IsEmpty())

            {

                pSysMenu->AppendMenu(MF_SEPARATOR);                  pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX, strAboutMenu);

            }

        }

  SetIcon(m_hIcon, TRUE);         // Set big icon

  SetIcon(m_hIcon, FALSE);    // Set small icon

  //弹出初始话对话框

  CInitDlg dlg;

  if (dlg.DoModal() == IDOK)

  {

     //创建监听套节字

     m_pSocket = new CListeningSocket(this);

     if (m_pSocket->Create(dlg.m_nPort+700))

     {

         if (m_pSocket->Listen())

            return TRUE;

     }

     else

         return FALSE;

  }

  else

     return FALSE;

  return TRUE;  // return TRUE  unless you set the focus to a control

}

五、运行结果如下:

  1.服务端初始化, 进入聊天室界面:

3.客户端初始化,端口号与服务器对应:

4. 进入聊天界面:

5.聊天室显示进入的客户:

6.另一个客户进入聊天室:

7.在客户界面上显示聊天界面:

  (芳菲燕)

(卡罗)

8.在聊天室界面上显示所有客户的聊天记录:

9.显示芳菲燕离开聊天室:

10.在芳菲燕的聊天界面上显示离开状态:

11.显示芳菲燕又进入聊天室:

六、课程设计心得:

我做的是“聊天室”系统,这个程序是和钟宏运同学合作的,他教会了我很多东西。通过做这个系统,我巩固和运用了很多课上学过的知识,也学会了很多课上没有讲过的知识,感受到了解课外知识的重要性,可谓是受益匪浅。

   在最开始建立工程时就犯了一个很大的错误,间错了工程,以致在建类时总是出错,浪费了几个小时才发现要建基于对话框的程序。这就体现了上课听讲的重要性,由于一时的疏忽又很有可能造成无可挽回的错误。我们做的系统很小,如果这是一个巨大工程,犯了这样一个错误,那是致命的。但是,话说回来,这不叫浪费时间,我们要吃一堑长一智,下次要记住,做工程,从第一步就应该认认真真,踏踏实实,避免出错。已进入工程,首先就做好了所有的界面设计。其中又遇到了很多困难。在插入位图时,要调整位图的大小,这是我不知道的,通过参考书和讨论终于完成这一步。从中,我更加坚定了讨论的重要作用。  其次,代码部分很多很复杂,但是结构确是很清晰的。我想这应该是MFC的一个很大的优点。根据参考书上的讲解和自己的揣摩,我明白了整个算法的灵魂。

这个程序讲的是通信,互联网上关于聊天功能的工具非常多,但是其实现技术都是大同小异。我们做的这个系统是用WINSOCK实现的最简单的聊天,多人能够发送信息至服务器,服务器首先显示进入聊天室的成员名,然后显示对方名字和聊天信息,而且可以并发执行。把通信的思想转化成语言的过程,靠的是C++的基础。实现过程中犯了很多错误,真的很后悔基础功不牢固,现在就要付出更多的时间来弥补。最近有很多专业课的要做课程设计,每一次任务的完成,都会很有成就感,发现经验真的很宝贵。有些人,可以随口说出那些常用的函数,走了一条捷径,而不知道的人,就要花时间去找那个函数,或者说自己写代码来实现那个函数的功能。所以,从现在开始,我会认真面对每一次编程,即使是小程序,也一样能锻炼人。

到这,这个学期的MFC学习结束了,但对知识的追求还没有结束,我们会的只是知识海洋里的一瓢水,还有更多的东西需要我们去钻研。不能说自己会有多高的觉悟,但小小的收益是无可否认的。

最后,谢谢景老师本学期的精心教导!

相关推荐