文章目录

  • 一、实验目的
  • 二、实验环境
  • 三、实验内容

一、实验目的

  1. 掌握MFC套接字编程方法
  2. 借助VS2016的MFC应用程序向导创建程序框架;
  3. 从CSocket类派生用户自定义的套接字类;
  4. 通过CArchive类、CSocketFile类、CSocket类实现网络数据交换。

二、实验环境

  1. 操作系统:WINDOWS 7及以上
  2. 开发工具:Microsoft VisualBasic6.0
  3. 实验设备:PC

三、实验内容

  MFC框架下设计一个聊天室服务器和多个聊天室客户机,实现多客户机并发的群聊功能,在服务器端需要用链表动态管理与客户机连接的套接字,实时更新服务器和客户机群的界面显示,其基本功能如下:
  1.要求服务器能与多个客户机建立连接,同时为多个客户机服务。

  2.服务器相当于聊天室大厅,它发布所有客户机的发言,并将客户机发言转发给其他客户机,从而间接实现客户机之间的通信。

  3.服务器动态统计进入聊天室的客户机数目,当有新客户机加入或退出时,试试更新在线客户数量。

客户机
Client.h:

// Client.h : PROJECT_NAME 应用程序的主头文件
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif
#include "resource.h"     // 主符号
class CClientApp : public CWinApp
{public:CClientApp();
public:virtual BOOL InitInstance();DECLARE_MESSAGE_MAP()
};
extern CClientApp theApp;

Client.cpp:

// Client.cpp : 定义应用程序的类行为。
//#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#endif// CClientAppBEGIN_MESSAGE_MAP(CClientApp, CWinApp)ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()// CClientApp 构造CClientApp::CClientApp()
{// 支持重新启动管理器m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;// TODO: 在此处添加构造代码,// 将所有重要的初始化放置在 InitInstance 中
}// 唯一的一个 CClientApp 对象CClientApp theApp;// CClientApp 初始化BOOL CClientApp::InitInstance()
{// 如果一个运行在 Windows XP 上的应用程序清单指定要// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,//则需要 InitCommonControlsEx()。否则,将无法创建窗口。INITCOMMONCONTROLSEX InitCtrls;InitCtrls.dwSize = sizeof(InitCtrls);// 将它设置为包括所有要在应用程序中使用的// 公共控件类。InitCtrls.dwICC = ICC_WIN95_CLASSES;InitCommonControlsEx(&InitCtrls);CWinApp::InitInstance();if (!AfxSocketInit()){AfxMessageBox(IDP_SOCKETS_INIT_FAILED);return FALSE;}AfxEnableControlContainer();// 创建 shell 管理器,以防对话框包含// 任何 shell 树视图控件或 shell 列表视图控件。CShellManager *pShellManager = new CShellManager;// 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));// 标准初始化// 如果未使用这些功能并希望减小// 最终可执行文件的大小,则应移除下列// 不需要的特定初始化例程// 更改用于存储设置的注册表项// TODO: 应适当修改该字符串,// 例如修改为公司或组织名SetRegistryKey(_T("应用程序向导生成的本地应用程序"));CClientDlg dlg;m_pMainWnd = &dlg;INT_PTR nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: 在此放置处理何时用//  “确定”来关闭对话框的代码}else if (nResponse == IDCANCEL){// TODO: 在此放置处理何时用//  “取消”来关闭对话框的代码}else if (nResponse == -1){TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");}// 删除上面创建的 shell 管理器。if (pShellManager != NULL){delete pShellManager;}// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,//  而不是启动应用程序的消息泵。return FALSE;
}

ClientDlg.h:

// ClientDlg.h : 头文件
//#pragma once
#include "ClientSocket.h" //手动添加包含语句// CClientDlg 对话框
class CClientDlg : public CDialogEx
{public:CClientDlg(CWnd* pParent = NULL);   // 标准构造函数// 对话框数据enum { IDD = IDD_CLIENT_DIALOG };
protected:virtual void DoDataExchange(CDataExchange* pDX); protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()
public://以下代码通过类向导添加CString m_strServerName;int m_nServerPort;CString m_strSpeaking;CString m_strUserName;CListBox m_listCRoom;afx_msg void OnClickedButtonLogin();afx_msg void OnClickedButtonLogout();afx_msg void OnClickedButtonSpeak();afx_msg void OnDestroy();CClientSocket* m_pSocket;CSocketFile* m_pFile;CArchive* m_pArchiveIn;CArchive* m_pArchiveOut;void onReceive(void);void ReceiveMessage(void);void SendMyMessage(CString& strMessage,BOOL bClosed);
};

ClientDlg.cpp:

// ClientDlg.cpp : 实现文件
//#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#include "afxdialogex.h"
#include "ClientSocket.h"//手动添加包含语句
#include "Message.h"// 用于应用程序“关于”菜单项的 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()// CClientDlg 对话框
CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/): CDialogEx(CClientDlg::IDD, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);//类向导自动添加的初始化代码m_strServerName = _T("");m_nServerPort = 0;m_strSpeaking = _T("");m_strUserName = _T("");//手动添加的初始化代码m_pSocket=NULL;m_pFile=NULL;m_pArchiveIn=NULL;m_pArchiveOut=NULL;
}void CClientDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Text(pDX, IDC_EDIT_SERVERNAME, m_strServerName);DDX_Text(pDX, IDC_EDIT_SERVERPORT, m_nServerPort);DDV_MinMaxInt(pDX, m_nServerPort, 1024, 49151);DDX_Text(pDX, IDC_EDIT_SPEAKING, m_strSpeaking);DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName);DDX_Control(pDX, IDC_LIST_CROOM, m_listCRoom);
}BEGIN_MESSAGE_MAP(CClientDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CClientDlg::OnClickedButtonLogin)ON_BN_CLICKED(IDC_BUTTON_LOGOUT, &CClientDlg::OnClickedButtonLogout)ON_BN_CLICKED(IDC_BUTTON_SPEAK, &CClientDlg::OnClickedButtonSpeak)ON_WM_DESTROY()
END_MESSAGE_MAP()// CClientDlg 消息处理程序
BOOL CClientDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// 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: 在此添加额外的初始化代码//手动添加如下初始化代码:m_strUserName=_T("I024Ha");m_strServerName=_T("localhost");m_nServerPort=8888;UpdateData(FALSE);//更新对应控件数据GetDlgItem(IDC_EDIT_SPEAKING)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_LOGOUT)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_SPEAK)->EnableWindow(FALSE);return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。
void CClientDlg::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 CClientDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}
//以下所有函数的框架由类向导生成,其实现代码需要手动添加
void CClientDlg::OnClickedButtonLogin()
{// TODO: 在此添加控件通知处理程序代码m_pSocket=new CClientSocket(this);//创建套接字if (!m_pSocket->Create()){//错误处理delete m_pSocket;m_pSocket=NULL;AfxMessageBox(_T("创建连接服务器的套接字错误,登录失败!"));return;}if (!m_pSocket->Connect(m_strServerName,m_nServerPort)){//错误处理delete m_pSocket;m_pSocket=NULL;AfxMessageBox(_T("连接服务器错误,登录失败!"));return;}m_pFile=new CSocketFile(m_pSocket);m_pArchiveIn=new CArchive(m_pFile,CArchive::load);m_pArchiveOut=new CArchive(m_pFile,CArchive::store);//向服务器发送消息,表明新客户进入聊天室UpdateData(TRUE);//更新控件成员变量CString strTemp;strTemp=m_strUserName+_T(":昂首挺胸进入聊天室!!!");SendMyMessage(strTemp,FALSE);GetDlgItem(IDC_EDIT_SPEAKING)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_LOGOUT)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_SPEAK)->EnableWindow(TRUE);GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(FALSE);GetDlgItem(IDC_EDIT_SERVERNAME)->EnableWindow(FALSE);GetDlgItem(IDC_EDIT_SERVERPORT)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_LOGIN)->EnableWindow(FALSE);}
//单击退出按钮的响应函数
void CClientDlg::OnClickedButtonLogout()
{// TODO: 在此添加控件通知处理程序代码CString strTemp;strTemp=m_strUserName+_T(":大步流星离开聊天室......");SendMyMessage(strTemp,TRUE);//删除对象,释放空间delete m_pArchiveIn;delete m_pArchiveOut;delete m_pFile;delete m_pSocket;m_pArchiveIn=NULL;m_pArchiveOut=NULL;m_pFile=NULL;m_pSocket=NULL;//清除聊天室内容while (m_listCRoom.GetCount()!=0) m_listCRoom.DeleteString(0);GetDlgItem(IDC_EDIT_SPEAKING)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_LOGOUT)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_SPEAK)->EnableWindow(FALSE);GetDlgItem(IDC_EDIT_USERNAME)->EnableWindow(TRUE);GetDlgItem(IDC_EDIT_SERVERNAME)->EnableWindow(TRUE);GetDlgItem(IDC_EDIT_SERVERPORT)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_LOGIN)->EnableWindow(TRUE);
}//单击发言按钮的响应函数
void CClientDlg::OnClickedButtonSpeak()
{// TODO: 在此添加控件通知处理程序代码UpdateData(TRUE);//更新控件成员变量,取回用户输入的数据if (!m_strSpeaking.IsEmpty()) //发言输入框不空{SendMyMessage(m_strUserName+"大声说:"+m_strSpeaking,FALSE);m_strSpeaking=_T("");UpdateData(FALSE);//更新用户界面,发言框清空}
}
//关闭客户机时的善后处理函数
void CClientDlg::OnDestroy()
{CDialogEx::OnDestroy();// TODO: 在此处添加消息处理程序代码if ((m_pSocket!=NULL) && (m_pFile!=NULL) && (m_pArchiveOut!=NULL)){CMessage msg;CString strTemp;strTemp=_T("广而告之:")+m_strUserName+_T("所在客户机已关闭");msg.m_strMessage=strTemp;msg.m_bClosed=TRUE;msg.Serialize(*m_pArchiveOut);m_pArchiveOut->Flush();//删除对象,释放空间delete m_pArchiveIn;delete m_pArchiveOut;delete m_pFile;m_pArchiveIn=NULL;m_pArchiveOut=NULL;m_pFile=NULL;if (m_pSocket!=NULL){BYTE buffer[100];m_pSocket->ShutDown();while (m_pSocket->Receive(buffer,100)>0);}delete m_pSocket;m_pSocket=NULL;}
}
//当套接字收到FD_READ消息时,它的OnReceive函数调用此函数
void CClientDlg::onReceive(void)
{do {ReceiveMessage();//接收消息if (m_pSocket==NULL) return;}while(!m_pArchiveIn->IsBufferEmpty());
}
//接收消息处理函数
void CClientDlg::ReceiveMessage(void)
{CMessage msg;TRY {msg.Serialize(*m_pArchiveIn);//接收m_listCRoom.AddString(msg.m_strMessage);//显示在大厅}CATCH(CFileException, e) { CString strTemp;strTemp=_T("与服务器连接已断开,连接关闭!");m_listCRoom.AddString(strTemp);msg.m_bClosed=TRUE;m_pArchiveOut->Abort();delete m_pArchiveIn;delete m_pArchiveOut;delete m_pFile;delete m_pSocket;m_pArchiveIn=NULL;m_pArchiveOut=NULL;m_pFile=NULL;m_pSocket=NULL;}END_CATCH
}//发送消息的处理函数
void CClientDlg::SendMyMessage(CString& strMessage,BOOL bClosed)
{if (m_pArchiveOut!=NULL) {CMessage msg;msg.m_strMessage=strMessage;msg.m_bClosed=bClosed;msg.Serialize(*m_pArchiveOut);m_pArchiveOut->Flush();}
}

ClientSocket.h:

#pragma once
class CClientDlg;  //对话框类声明,手动添加
class CClientSocket : public CSocket
{public:CClientSocket(CClientDlg* pDlg);//为构造函数添加入口参数,手动添加virtual ~CClientSocket();//下面两行由类向导生成CClientDlg* m_pDlg;//成员变量virtual void OnReceive(int nErrorCode);
};

ClientSocket.cpp:

// ClientSocket.cpp : 实现文件
#include "stdafx.h"
#include "Client.h"
#include "ClientSocket.h"
#include "ClientDlg.h"  //手动添加的包含语句
CClientSocket::CClientSocket(CClientDlg* pDlg)
{m_pDlg=pDlg;
}
CClientSocket::~CClientSocket()
{m_pDlg=NULL;
}
// CClientSocket 成员函数
//事件处理函数,当客户端套接字收到FD_READ消息时,执行此函数
void CClientSocket::OnReceive(int nErrorCode)
{// TODO: 在此添加专用代码和/或调用基类CSocket::OnReceive(nErrorCode);//调用CClientDlg类的相应函数处理if (nErrorCode==0) m_pDlg->onReceive();
}

Message.h:

// CMessage定义
#pragma once
class CMessage : public CObject
{public:CMessage();virtual ~CMessage();CString m_strMessage;BOOL m_bClosed;virtual void Serialize(CArchive& ar);
};

Message.cpp:

// Message.cpp : 实现文件
#include "stdafx.h"
#include "Client.h"
#include "Message.h"
CMessage::CMessage()
{m_strMessage = _T("");m_bClosed=FALSE;
}
CMessage::~CMessage()
{}
// CMessage 成员函数
void CMessage::Serialize(CArchive& ar)
{if (ar.IsStoring()){   // 发送数据ar<<(WORD)m_bClosed;ar<<m_strMessage;}else{  // 接收数据WORD wd;ar>>wd;m_bClosed=(BOOL)wd;ar>>m_strMessage;}
}

服务器
CClientSocket.h:

// CClientSocket定义
#pragma once
class CServerDlg;
class CMessage;class CClientSocket : public CSocket
{public:CClientSocket(CServerDlg* pDlg);//为构造函数增加入口参数virtual ~CClientSocket();//重载回调函数,套接字收到数据时,自动调用此函数virtual void OnReceive(int nErrorCode);CServerDlg* m_pDlg;//定义指向主对话框类的指针CSocketFile* m_pFile; //定义指向CSocketFile对象的指针CArchive* m_pArchiveIn;//定义指向输入CArchive对象的指针CArchive* m_pArchiveOut;//定义指向输出CArchive对象的指针void SendMessage(CMessage* pMsg);//发送消息void ReceiveMessage(CMessage* pMsg);//接收消息void Init(void);//初始化
};

ClientSocket.cpp:

// ClientSocket.cpp : 实现文件
#include "stdafx.h"
#include "Server.h"
#include "ClientSocket.h"
#include "ServerDlg.h" //手动添加包含语句
#include "Message.h"  //手动添加包含语句
// CClientSocket
CClientSocket::CClientSocket(CServerDlg* pDlg)//增加入口参数,手动添加
{   //初始化成员变量,手动添加m_pDlg=pDlg;m_pFile=NULL;m_pArchiveIn=NULL;m_pArchiveOut=NULL;
}
CClientSocket::~CClientSocket()
{//置空或释放成员变量,手动添加m_pDlg=NULL;if (m_pFile!=NULL) delete m_pFile;if (m_pArchiveIn!=NULL) delete m_pArchiveIn;if (m_pArchiveOut!=NULL) delete m_pArchiveOut;m_pFile=NULL;m_pArchiveIn=NULL;m_pArchiveOut=NULL;
}
// CClientSocket 成员函数
//套接字收到数据时,自动调用此函数
void CClientSocket::OnReceive(int nErrorCode)
{// TODO: 在此添加专用代码和/或调用基类CSocket::OnReceive(nErrorCode);m_pDlg->onReceive(this);//调用主对话框中的处理函数,手动添加
}
void CClientSocket::Init(void)
{   //手动添加初始化代码m_pFile=new CSocketFile(this,TRUE);m_pArchiveIn=new CArchive(m_pFile,CArchive::load);m_pArchiveOut=new CArchive(m_pFile,CArchive::store);
}
//发送消息
void CClientSocket::SendMessage(CMessage* pMsg)
{//手动添加if (m_pArchiveOut!=NULL){pMsg->Serialize(*m_pArchiveOut);m_pArchiveOut->Flush();}
}
//接收消息
void CClientSocket::ReceiveMessage(CMessage* pMsg)
{pMsg->Serialize(*m_pArchiveIn);
}

Message.h:

// CMessage定义
#pragma once
class CMessage : public CObject
{public:CMessage();virtual ~CMessage();CString m_strMessage;//字符串消息BOOL m_bClosed;//是否关闭virtual void Serialize(CArchive& ar);//重载基类序列化函数
};

Message.cpp:

// Message.cpp : 实现文件
#include "stdafx.h"
#include "Server.h"
#include "Message.h"CMessage::CMessage()
{m_strMessage = _T("");//类向导自动添加m_bClosed=FALSE;//手动添加
}
CMessage::~CMessage()
{}// CMessage 成员函数
//类向导自动添加
void CMessage::Serialize(CArchive& ar)
{if (ar.IsStoring()){   // 发送数据代码,手动添加ar<<(WORD)m_bClosed;ar<<m_strMessage;}else{    // 接收数据代码,手动添加WORD wd;ar>>wd;m_bClosed=(BOOL)wd;ar>>m_strMessage;}
}

Server.h:

// Server.h : PROJECT_NAME 应用程序的主头文件
#pragma once
#ifndef __AFXWIN_H__
#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif#include "resource.h"       // 主符号
// CServerApp:
// 有关此类的实现,请参阅 Server.cpp
//
class CServerApp : public CWinApp
{public:CServerApp();// 重写
public:virtual BOOL InitInstance();// 实现DECLARE_MESSAGE_MAP()
};
extern CServerApp theApp;

Server.cpp:

// Server.cpp : 定义应用程序的类行为。
#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CServerApp
BEGIN_MESSAGE_MAP(CServerApp, CWinApp)ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()
// CServerApp 构造
CServerApp::CServerApp()
{// 支持重新启动管理器m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;// TODO: 在此处添加构造代码,// 将所有重要的初始化放置在 InitInstance 中
}
// 唯一的一个 CServerApp 对象
CServerApp theApp;
// CServerApp 初始化
BOOL CServerApp::InitInstance()
{INITCOMMONCONTROLSEX InitCtrls;InitCtrls.dwSize = sizeof(InitCtrls);// 将它设置为包括所有要在应用程序中使用的// 公共控件类。InitCtrls.dwICC = ICC_WIN95_CLASSES;InitCommonControlsEx(&InitCtrls);CWinApp::InitInstance();if (!AfxSocketInit()){AfxMessageBox(IDP_SOCKETS_INIT_FAILED);return FALSE;}AfxEnableControlContainer();// 创建 shell 管理器,以防对话框包含// 任何 shell 树视图控件或 shell 列表视图控件。CShellManager *pShellManager = new CShellManager;SetRegistryKey(_T("应用程序向导生成的本地应用程序"));CServerDlg dlg;m_pMainWnd = &dlg;INT_PTR nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: 在此放置处理何时用//  “确定”来关闭对话框的代码}else if (nResponse == IDCANCEL){// TODO: 在此放置处理何时用//  “取消”来关闭对话框的代码}// 删除上面创建的 shell 管理器。if (pShellManager != NULL){delete pShellManager;}// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,//  而不是启动应用程序的消息泵。return FALSE;
}

ServerDlg.h:

// ServerDlg.h : 头文件
#pragma once
#include "ServerSocket.h"
#include "ClientSocket.h"
class CMessage;
// CServerDlg 对话框
class CServerDlg : public CDialogEx
{public:CServerDlg(CWnd* pParent = NULL);   // 标准构造函数// 对话框数据enum { IDD = IDD_SERVER_DIALOG };
protected:virtual void DoDataExchange(CDataExchange* pDX);  // DDX/DDV 支持
protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()//以下代码通过类向导手动添加
public:int m_nServerPort;CListBox m_listSroom;CStatic m_staOnline;afx_msg void OnClickedButtonStart();afx_msg void OnClickedButtonStop();CServerSocket* m_pServerSocket;//侦听套接字指针变量CPtrList m_ClientsList; //在线客户机链表void onAccept(void);//处理客户机连接请求,从CServerSocket类的OnAccept函数转到此处执行void onReceive(CClientSocket* pSocket);//获取客户机发送的数据,从CClientSocket类的OnReceive函数转到此处执行void sendToClients(CMessage* pMsg);//服务器向所有客户机转发消息
};

ServerDlg.cpp:

// ServerDlg.cpp : 实现文件
#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#include "afxdialogex.h"
#include "Message.h"  //手动添加包含语句
// 用于应用程序“关于”菜单项的 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()
// CServerDlg 对话框
CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/): CDialogEx(CServerDlg::IDD, pParent)
{m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);m_nServerPort = 0;//类向导添加的成员变量初始化代码m_pServerSocket=NULL;//手动添加
}
void CServerDlg::DoDataExchange(CDataExchange* pDX)
{CDialogEx::DoDataExchange(pDX);DDX_Text(pDX, IDC_EDIT_SERVERPORT, m_nServerPort);DDV_MinMaxInt(pDX, m_nServerPort, 1024, 49151);DDX_Control(pDX, IDC_LIST_SROOM, m_listSroom);DDX_Control(pDX, IDC_STATIC_ONLINE, m_staOnline);
}
BEGIN_MESSAGE_MAP(CServerDlg, CDialogEx)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON_START, &CServerDlg::OnClickedButtonStart)ON_BN_CLICKED(IDC_BUTTON_STOP, &CServerDlg::OnClickedButtonStop)
END_MESSAGE_MAP()
// CServerDlg 消息处理程序
BOOL CServerDlg::OnInitDialog()
{CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// 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: 在此添加额外的初始化代码m_nServerPort=8888;UpdateData(FALSE);//用成员变量值更新界面GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);return TRUE;  // 除非将焦点设置到控件,否则返回TRUE
}
void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialogEx::OnSysCommand(nID, lParam);}
}
void CServerDlg::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 CServerDlg::OnQueryDragIcon()
{return static_cast<HCURSOR>(m_hIcon);
}
//单击启动服务器按钮的事件处理函数
void CServerDlg::OnClickedButtonStart()
{// TODO: 在此添加控件通知处理程序代码UpdateData(TRUE);//获得用户输入给成员变量//创建服务器套接字对象,用于在指定端口侦听m_pServerSocket=new CServerSocket(this);if (!m_pServerSocket->Create(m_nServerPort)){//错误处理delete m_pServerSocket;m_pServerSocket=NULL;AfxMessageBox(LPCTSTR("创建服务器侦听套接字出现错误!"));return;}//启动服务器侦听套接字,可以随时接收来自客户机的请求if (!m_pServerSocket->Listen()){//错误处理delete m_pServerSocket;m_pServerSocket=NULL;AfxMessageBox(LPCTSTR("启动服务器侦听套接字出现错误!"));return;}GetDlgItem(IDC_EDIT_SERVERPORT)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);
}
//单击停止服务器按钮的事件处理函数
void CServerDlg::OnClickedButtonStop()
{// TODO: 在此添加控件通知处理程序代码CMessage msg;msg.m_strMessage="服务器已停止侦听服务!";delete m_pServerSocket;//释放服务器侦听套接字m_pServerSocket=NULL;//清除客户机链接列表while(!m_ClientsList.IsEmpty()){//向每一个客户机发送"服务器已停止侦听服务!"这个消息并从列表中删除链接,释放资源CClientSocket* pSocket=(CClientSocket*)m_ClientsList.RemoveHead();pSocket->SendMessage(&msg);delete pSocket;}//清除服务器聊天室大厅while(m_listSroom.GetCount()!=0)m_listSroom.DeleteString(0);GetDlgItem(IDC_EDIT_SERVERPORT)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
}
//服务器处理来自客户机的连接请求并在服务器端维护一个链接列表
void CServerDlg::onAccept(void)
{//创建服务器端连接客户机的套接字CClientSocket* pSocket=new CClientSocket(this);if (m_pServerSocket->Accept(*pSocket)){//建立客户机连接,加入客户机链接列表pSocket->Init();m_ClientsList.AddTail(pSocket);//更新在线人数CString strTemp;strTemp.Format(_T("当前在线人数:%d"),m_ClientsList.GetCount());m_staOnline.SetWindowTextW(strTemp);}else{delete pSocket;pSocket=NULL;}
}
//服务器处理来自客户机的消息
void CServerDlg::onReceive(CClientSocket* pSocket)
{static CMessage msg;do {pSocket->ReceiveMessage(&msg);//接收消息m_listSroom.AddString(msg.m_strMessage);//加入服务器列表框sendToClients(&msg);//转发给所有客户机//如果客户机关闭,从链接列表删除服务器端与之会话的链接套接字if (msg.m_bClosed){//pSocket->Close();POSITION pos,temp;for(pos=m_ClientsList.GetHeadPosition();pos!=NULL;){temp=pos;CClientSocket* pTempSocket=(CClientSocket*)m_ClientsList.GetNext(pos);if (pTempSocket==pSocket){m_ClientsList.RemoveAt(temp);CString strTemp;//更新在线人数strTemp.Format(_T("当前在线人数:%d"),m_ClientsList.GetCount());m_staOnline.SetWindowTextW(strTemp);break;}//end if}//end fordelete pSocket;pSocket=NULL;break;}//end if}while(!((pSocket->m_pArchiveIn)->IsBufferEmpty()));
}
//服务器向所有客户机转发来自某一客户机的消息
void CServerDlg::sendToClients(CMessage* pMsg)
{for (POSITION pos=m_ClientsList.GetHeadPosition();pos!=NULL;){CClientSocket* pSocket1=(CClientSocket*)m_ClientsList.GetNext(pos);pSocket1->SendMessage(pMsg);}
}

ServerSocket.h:

// CServerSocket定义
#pragma once
class CServerDlg; //声明服务器对话框类
class CServerSocket : public CSocket
{public:CServerSocket(CServerDlg* pDlg);//添加入口参数virtual ~CServerSocket();//回调函数,套接字收到连接请求时,自动调用此函数virtual void OnAccept(int nErrorCode);CServerDlg* m_pDlg; //指向服务器对话框类的指针
};

ServerSocket.cpp:

// ServerSocket.cpp : 实现文件
#include "stdafx.h"
#include "Server.h"
#include "ServerSocket.h"
#include "ServerDlg.h"  //手动添加
CServerSocket::CServerSocket(CServerDlg* pDlg)
{m_pDlg=pDlg;//初始化成员变量
}
CServerSocket::~CServerSocket()
{m_pDlg=NULL;
}// CServerSocket 成员函数
void CServerSocket::OnAccept(int nErrorCode)
{// TODO: 在此添加专用代码和/或调用基类CSocket::OnAccept(nErrorCode);m_pDlg->onAccept();//调用主对话框中的处理函数
}

运行结果截图:
服务器:

客户机:

聊天室:


【通信协议及编码】实验3:MFC框架下聊天室客户机与服务器程序相关推荐

  1. 基于MFC框架下OpenGL的exe可移植

    以前写了几个MFC框架下的OpenGL小项目,但是移植exe的时候总需要copy很多dll文件,感觉太low,于是今天就抽时间解决了这个问题. 首先 我们了解一下关于MFC的两个运行机制: 一.MFC ...

  2. VC 在经典 MFC 框架下使用真彩色工具栏

    VS2008 sp1 以后有了 BCG 的包,可以使用 CMFCToolbar 来加载真彩色工具栏.但是在经典 MFC 框架下,默认还只是 16 色的工具栏,通过下面的方法就可以加载真彩工具栏了.直接 ...

  3. 关于MFC框架下的TextOut()函数输出变量的值

    关于MFC框架下的TextOut()函数输出变量的值(int转String类型) 一.TextOut()参数介绍 BOOL TextOut ( //如果函数调用成功,返回TRUE,否则,返回FALSE ...

  4. MFC框架下自绘CEdit控件

    前提 MFC中的CEdit控件提供了编辑的功能. 该编辑框是一个自创阔举行,用户可以向里面输入任意类型的文本信息. 那么我们该如何优化控件的显示呢?接下来就介绍下如何继承并重绘CEdit控件. 该控件 ...

  5. 基于TCP协议的mfc多人聊天室

    基于TCP协议的MFC多人聊天室 简易的mfc聊天室思路实现 前言 一.socket通信 二.效果展示 三.代码实现 1.ClientSocket.cpp 客户端套接字 2.ServerSocket. ...

  6. DirectX 在MFC框架下的应用

    一般而言,用到DirectX进行编程时,会直接在win32项目下自己创建窗口,然后在该窗口上进行操作.然而,如果已经用MFC写了一部分代码,然后突然发现用DirectX来处理3D的显示部分会比较给力, ...

  7. 在MFC框架下使用osg报内存泄露的解决办法

    作者:朱金灿 来源:http://blog.csdn.net/clever101/ 最近正在学习osg,从osgchina网站下下载了osg的debug版本库,具体地址为: OpenSceneGrap ...

  8. 实验三 实现私聊群聊聊天室

    一.实验目的 1.掌握各个内置对象的含义: 2.理解并熟练应用session.application对象. 二.实验内容 1.设计聊天室,在聊天室中,需要通过JSP内置对象application来实时 ...

  9. 单片机双机通信c语言实验心得,80C51单片机双机通信与多机通信的使用方法

    80C51单片机之间的串行通信主要可分为双机通信和多机通信,本节举例说明双机通信与多机通信的使用方法. 1.双机通信硬件电路如果两个80C51单片机系统距离较近,那么就可以将它们的串行口直接相连,实现 ...

最新文章

  1. 怎么寻回执行页内操作时的错误磁盘的数据
  2. Quake3中的绝对值函数
  3. 从零开始入门 K8s| 阿里技术专家详解 K8s 核心概念
  4. Windows10上安装Git 2.10.2详细图解
  5. Executor框架、ThreadPoolExecutor、3种常见的线程池
  6. Linux运维必备的40个命令总结(值得收藏)
  7. 想转行人工智能?哈佛博士后有话说!
  8. linux c mysql教程_linux下c操作mysql之增删改查
  9. jQuery -- 光阴似箭(五):AJAX 方法
  10. Linux查看二进制文件hexeditor
  11. Linux下shell命令 1
  12. 数智德州,创新未来 | 智慧城市赛题上线山东大赛德州分赛场
  13. mysql 5.7 多线程主从复制_Mysql 5.7从节点配置多线程主从复制的方法详解
  14. 11.深入分布式缓存:从原理到实践 --- Aerospike原理及广告业务应用
  15. BZOJ2832[AHOI2012] 信号塔
  16. SQL Server 2016安装教程
  17. 第二章 ARM体系结构与指令集——ARM
  18. csapp bomb lab (《深入理解计算机系统》lab2)
  19. pc端生成支付宝支付二维码
  20. “讯飞杯”合肥市第三十届青少年信息学奥林匹克竞赛(小学组)试题

热门文章

  1. jenkins UNSTABLE
  2. 文件分段上传案列(参考b站小野森森老师的课)
  3. 电脑已连接WIFI,但是现实网络不可用
  4. 性能测试 理论初探(一),什么是性能测试?性能测试的目的是?
  5. cocos2dx[3.2](2) 3.x巨变
  6. centos7重启nginx
  7. x265-1.7版本-common/intrapred.cpp注释
  8. 【Java 集合】集合框架 JCF
  9. JCF 集合框架小结
  10. CC00065.kafka——|Hadoopkafka.V50|——|kafka.v50|日志清理|