【通信协议及编码】实验3:MFC框架下聊天室客户机与服务器程序
生活随笔
收集整理的這篇文章主要介紹了
【通信协议及编码】实验3:MFC框架下聊天室客户机与服务器程序
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 一、實驗目的
- 二、實驗環境
- 三、實驗內容
一、實驗目的
二、實驗環境
三、實驗內容
??MFC框架下設計一個聊天室服務器和多個聊天室客戶機,實現多客戶機并發的群聊功能,在服務器端需要用鏈表動態管理與客戶機連接的套接字,實時更新服務器和客戶機群的界面顯示,其基本功能如下:
??1.要求服務器能與多個客戶機建立連接,同時為多個客戶機服務。
??2.服務器相當于聊天室大廳,它發布所有客戶機的發言,并將客戶機發言轉發給其他客戶機,從而間接實現客戶機之間的通信。
??3.服務器動態統計進入聊天室的客戶機數目,當有新客戶機加入或退出時,試試更新在線客戶數量。
客戶機
Client.h:
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:
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框架下聊天室客户机与服务器程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学生成绩排名问题
- 下一篇: 移动版PC版微信登陆功能