局域网聊天程序设计
更新时间:2023-10-01 04:25:01 阅读量: 综合文库 文档下载
青岛理工大学 C++面向对象课程设计报告
院(系): 计算机工程学院 专业: 软件工程
学生姓名: 管巨伟
班级 软件132 学号: 201307227 题目: 局域网聊天程序设计 ____
起迄日期: _ 2015.6.29~2015.7.10
设计地点: 计算机学院机房
指 导 教 师: 李传斌 杨鑫
完成日期: 2015 年7月 10 日
任务书
一、课程设计目的与要求
1.课程设计目的
面向对象程序设计作为一门软件设计的课程,具有极强的实践性,必须使学生具备灵活应用理论知识的能力及面向对象程序设计技能。所以在《C++面向对象程序设计》课程学习完成后,安排课程设计教学环节。
通过课程设计,使学生实际掌握面向对象的程序设计方法,了解C++面向对象的设计方法与技巧,有效地、深刻地理解课程内容,体会理论、方法和设计原则;培养学生分析实际问题和解决问题的能力,使学生具备使用面向对象程序设计开发工具设计实际系统的能力。
2.课程设计要求
结构化程序设计使用的是功能抽象,面向对象程序设计不仅能进行功能抽象,而且能进行数据抽象。“对象”实际上是功能抽象和数据抽象的统一。C++语言的“对象”是“类”的实例,程序设计的基础是设计类,所以类的有关概念都是重点,尤其要抓住抽象、封装、继承和多态性等要素。
面向对象程序设计的核心是类层次的设计。而具体类的设计的重点是如何选择数据成员和成员函数。根据数据成员和成员函数的特点,结合具体问题设计合适的类。成员函数设计中的难点是选择函数类型及其参数传递方式。
开发系统离不开设计平台,学生应在掌握面向对象程序设计基础上,熟悉并能熟练使用面向对象程序设计开发平台,结合相关理论知识,进行相应系统开发。
二、课程设计内容
课程设计题目及要求
局域网聊天程序设计:服务端设置,通过对客户端多个线程的监听,显示客户端的上线、下线,与客户端进行信息交流。开启服务器是通过新建socket,绑定端口号,监听线程,等待客户端连入。创建动态数组,完成客户端的连入,存储客户端信息。结束线程,完成对资源释放。
1
课程设计报告内容 一、需求分析
1.选做此课题或项的目的
开发此聊天程序旨在供个人聊天交流使用,进行多人并发聊天,交流思想见解,让使用者充分体验网络即时工具的方便快捷。同时了解现今正在使用的交流软件的基础功能,了解网络编程的基础思想,了解线程的使用,多个任务同时进行的原理,拓展自己的视野,提升自己的编码能力。也想通过该课题了解MFC的编码过程,学习收获到客户端、服务端都包含的交互式界面的实现及应用。
2.程序所实现的功能
客户端:1、获取输入的IP地址,2、获取通信的端口号,3、获取从服务器发来的信息,4、给服务器发送用户自定义的数据。
服务端:1、获取客户端IP地址,2、获取通信的端口号,3、记录客户端连入的台数,4、向所有连入客户端发送消息,5、接收客户端发送过来的信息
其他:进行网络的设置、关闭运行的程序、发送消息、系统托盘、系统托盘下对程序的显示和退出操作。
二、内容设计
1.根据所选题目,给出模块图
主聊天界面 ChatRoomDlg
更多功能消息函数 OnBnClickedOther() 网络设置 消息函数 OnBnClickedNetset() 关闭程序窗口消息函数OnBnClickedCancel() 系统托盘消息函数OnMenuTrayinco() 客户端打开消息函数OnBnClickedStartClient() 服务器打开 消 息 函数 OnBnClickedStartServer() 服务端设置OnBnClickedRadioServer() 客户端设置OnBnClickedRadioClient() 发送消息消息函数 SendClientsMsg() 接收消息 CreatThread() 图1 模块图 2
2.画出主程序及其主要模块的流程图
开始 Y 网络设置 N Y 更多功能 服务端或客户端设置 N 系统托盘 发送消息 关闭窗口 结束 图2主程序流程图 3
Y N 客户端设置 开始 网络设置
N 开启客户端 输入IP地址和 端口号 绑定端口号 开启服务端端 Y 服务端开启
发送或接受消息 关闭线程、关闭窗口 结束 图3 网络设置及消息发送流程图 4
pChatRoom->m_ClientArray.GetAt(idx).hThread = tItem.hThread;//保存线程,方 //便之后的其他使用 ResumeThread(tItem.hThread);//恢复线程运行 CString strMsg;
strMsg = _T(\客户端:\进入聊天室!\pChatRoom->ShowMsg(strMsg);
pChatRoom->SendClientsMsg(strMsg, &tItem);//消息的转发 Sleep(100);
} }
__Error_End: closesocket(pChatRoom->m_ListenSock); return TRUE; }
DWORD WINAPI ClientThreadProc(LPVOID lpParameter)//客户端处理线程函数: { CString strMsg;
CClientItem m_ClientItem = *(CClientItem *)lpParameter;//复制传进来的结点避免以前结 //点被修
while( TRUE && !(m_ClientItem.m_pMainWnd->bShutDown)) {//一直监视连接进来的客户端 if ( SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE) ) {//判断缓冲区内是否有信息 TCHAR szBuf[MAX_BUF_SIZE] = {0};//定义一个缓冲区用于存储接收进来的信息 int iRet = recv(m_ClientItem.m_Socket, (char *)szBuf, MAX_BUF_SIZE, 0);//接收信息 if ( iRet > 0 ) {//接收成功 strMsg = szBuf; strMsg = _T(\客户端:\ m_ClientItem.m_pMainWnd->ShowMsg(strMsg); m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem); }else{//客户端关闭 strMsg = _T(\客户端:\离开了聊天室!\ m_ClientItem.m_pMainWnd->ShowMsg(strMsg); m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem); m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem); break; } } Sleep(500); } return TRUE; }
停止客户端、服务端:
void CChatRoomDlg::StopClient() { bShutDown = TRUE;
10
DWORD dwRet = WaitForSingleObject(m_hConnectThred, 1000);//等待线程结束的响应 if ( dwRet != WAIT_OBJECT_0 ) { TerminateThread(m_hConnectThred, -1);//强制结束线程 closesocket(m_ConnectSock); } m_hConnectThred = NULL; m_ConnectSock = INVALID_SOCKET; m_bIsServer = -1; bShutDown = FALSE; }
void CChatRoomDlg::StopServer() { UINT nCount = m_ClientArray.GetCount(); HANDLE *m_pHandles = new HANDLE[nCount+1];//额外的服务端线程,数组长度加1 m_pHandles[0] = m_hListenThread; for( int idx = 0; idx < nCount; idx++ ) { m_pHandles[idx+1] = m_ClientArray.GetAt(idx).hThread; } bShutDown = TRUE; DWORD dwRet = WaitForMultipleObjects(nCount+1, m_pHandles, TRUE, 1000); if ( dwRet != WAIT_OBJECT_0 ) { for( INT_PTR i = 0; i < m_ClientArray.GetCount(); i++ ) { TerminateThread(m_ClientArray.GetAt(i).hThread, -1); closesocket(m_ClientArray.GetAt(i).m_Socket); } TerminateThread(m_hListenThread, -1); closesocket(m_ListenSock); } delete [] m_pHandles; //释放数组资源 m_hListenThread = NULL; m_ListenSock = INVALID_SOCKET; m_bIsServer = -1; bShutDown = FALSE; }
系统托盘:
void CChatRoomDlg::OnBnClickedOther() { CPoint pt; CRect mRect; CMenu mMenu, *pMenu = NULL; GetDlgItem(IDC_OTHER)->GetWindowRect(&mRect);//取得更多功能按钮的位置 pt = mRect.BottomRight(); //获取按钮右下角的坐标 pt.y = mRect.top+10; mMenu.LoadMenu(IDR_MENU1); //加载菜单项
11
pMenu = mMenu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
//将菜单项显示在相应坐标位置
}
BOOL CChatRoomDlg::TrayMyIcon(BOOL bAdd) { BOOL bRet = FALSE; NOTIFYICONDATA tnd; tnd.cbSize = sizeof(NOTIFYICONDATA); tnd.hWnd = GetSafeHwnd(); //获得当前线程 tnd.uID = IDR_MAINFRAME; if ( bAdd == TRUE ) { tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; tnd.uCallbackMessage = WM_TRAYICON_MSG;//消息响应
tnd.hIcon=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME)); _tcscpy_s(tnd.szTip, sizeof(tnd.szTip), _T(\聊天室v1.0\鼠标移入显示信息 ShowWindow(SW_MINIMIZE); ShowWindow(SW_HIDE); bRet = Shell_NotifyIcon(NIM_ADD, &tnd);//显示系统托盘图标 }else{ ShowWindow(SW_SHOWNA); SetForegroundWindow(); bRet = Shell_NotifyIcon(NIM_DELETE, &tnd);//显示程序后,托盘图标消失 } return bRet; }
void CChatRoomDlg::OnMenuTrayinco() { TrayMyIcon(); }
LRESULT CChatRoomDlg::OnTrayCallBackMsg(WPARAM wparam, LPARAM lparam) { switch(lparam)//消息类型 { case WM_RBUTTONUP://右键单击 { CMenu mMenu, *pMenu = NULL; CPoint pt; mMenu.LoadMenu(IDR_MENU2); pMenu = mMenu.GetSubMenu(0); GetCursorPos(&pt);//获得坐标位置 SetForegroundWindow(); pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this); break;
12
} case WM_LBUTTONDBLCLK://左键双击 ShowWindow(SW_RESTORE); SetForegroundWindow(); TrayMyIcon(FALSE); break; default:break; } return NULL; }
void CChatRoomDlg::OnMenuShow() { ShowWindow(SW_RESTORE); TrayMyIcon(FALSE); }
void CChatRoomDlg::OnMenuExit() { TrayMyIcon(FALSE); OnBnClickedCancel(); }
三、调试分析
1.实际完成的情况说明(完成的功能,支持的数据类型等)。
本程序实现了基础的局域网聊天过程,通过网络的设置可以实现服务端和客户端的信息收发,同时支持一对多的收发信息,多个客户端可以并发和服务端进行数据的收发。为防止操作错误,提供了多个提醒方式。其中包括停止服务器,停止客户端,以及程序处于客户端、服务端、最初状态时的关闭提醒。另外添加了系统托盘功能,在暂时不使用时可先挂起。 实现程序过程中使用了包括库中包含的网络编程所需基本CString、Socket、HANDLE等数据,为对IP地址、端口号的处理,还包含了fd_set、sockaddr_in 等的结构,过程中为对进行判别状态,数据转换,还使用了BOOL、char、TCHAR等的中间转换及判断。
2.程序的性能分析。
本程序封装了多个函数,在使用时可反复调用,无需再重写相应代码。通过设计客户端类,将客户端独立分开,开启相应线程,无需等待上一个客户端停止即可与服务端进行数据收发,实现一对多的通信,提高了通信效率。开设多个线程时,关闭相应就复杂,需要将所有线程关闭,释放资源,增加了代码的复杂度。关闭线程时,本程序设置了标记,通过标记的执行来使程序将对应的线程自动释放,将该线程申请的资源释放完全。但在无法正常关闭的情况下,采用了强制关闭,可能会使申请的相应资源无法彻底释放。运行时并未出现太久等待,消息的响应较为快捷。同时在程序中使用动态数组存储客户端连入,不会为满足较多的连入量而定义较大数组,造成空间的浪费。还设置了多种操作提示,避免用户操作失误带来的不必要结果,安全性较为可观。总的来说,本程序综合考虑了代码的简洁性,类的分类清楚,资源的释放,空间的利用率高、运行效率高效、安全性能等多方面的优化,性能较为可观。
13
3.上机过程中出现的问题及其解决方案。
1、网络设置中,对RadioButton的选定和转换选定进行切换,操作的结果和预想的不一样,在网上查找后,在格式中先将对应一组的RadioButton进行编号,再将这组RadioButton的第一个的group属性设置为true,其他设置为false。
2、连接服务器响应时,accept()函数为阻塞式的,这就在启动服务器时等待客户端连接,无法正常进行接下来的步骤。添加了异步I/O模型,通过select()函数的使用,在缓冲区中查看是否有客户端的连接,可以将阻塞的问题解决。
3、可能会有多个客户端连接服务器,这个过程中每个客户端各自独立运行,在并行运行的问题上,通过对线程的创建,每个运行的程序单独开辟线程,使程序单独运行。
4、对客户端的连入,服务端应该详细知道其信息,上线或下线时在服务端应该添加删除相应客户端,通过使用动态式数组,定义客户端类,每连入一个客户端,即添加入数组中,下线即从数组中删除客户端。
5、程序多个地方会重用一个变量,如窗口指针、socket等的重用,如直接使用过程中可能会对其有所修改,使后面使用时出现非预想的结果。此时定义了对应变量来保存,使变量的使用更为方便。
6、程序关闭时,直接关闭窗口相应的线程要停止,以便对占用的资源释放。强制终止线程仍会有该线程申请的一些资源得不到释放的问题。此时设置了bool变量判定程序是否关闭,关闭时让对应线程自动停止释放资源。
7、系统托盘的设计,菜单栏的添加,运行时菜单栏出现在非预想位置。通过对CPoint的添加,获取更多功能按钮的位置,通过TrackPopupMenu的使用,将菜单显示在了更多按钮的右侧偏下的位置。
4.程序中可以改进的地方说明。
由于对新知识的学习,时间的限制,程序可以添加登录界面,当用户注册登录后进行数据的收发。收发数据时显示的为客户端的IP地址,可以相应改成客户端登录的昵称。还缺少对文件、图片、视频、语音的数据收发。系统托盘时的图标显示为默认图标,此时可以设置成具有程序特点的图标来表示。
5.程序中可以扩充的功能及设计实现构想。
用户登录界面可以直接添加一个窗口,设计好界面后,在按钮、文本编辑框等中添加响应代码,完成对聊天界面的调用即可。在聊天界面中,将用户登录的昵称获取,在聊天显示中,将字符串改成“昵称+所发消息”即可。添加流的操作,统一使用二进制流的操作,以便对图片、视频、语音的操作,通过输入输出流完成数据的收发。添加图片,在函数中将默认图片的名称改成自定义的图片,使系统托盘具有自己程序的特色。
四、用户手册
程序支持Windows7,Windows8.1,WindowsXP系统。
14
Sleep(500); }
__Error_End: closesocket(pChatRoom->m_ConnectSock);//关闭线程 return TRUE; }
服务端的线程函数: #include \
#include \
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)//去缓冲区中查看是否有socket连接 { fd_set fdset; timeval tv;//超时时间 FD_ZERO(&fdset);//清0 FD_SET(hSocket, &fdset); nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut; tv.tv_sec = 0;//以秒为单位 tv.tv_usec = nTimeOut;//一毫秒为单位
int iRet = 0; if ( bRead ) { iRet = select(0, &fdset, NULL , NULL, &tv);//监听可读文件 }else{ iRet = select(0, NULL , &fdset, NULL, &tv);//监听可写 }
if(iRet <= 0) { return FALSE; } else if (FD_ISSET(hSocket, &fdset)){//查看缓冲区中的socket是否准备好 return TRUE; } return FALSE; }
DWORD WINAPI ListenThreadFunc(LPVOID pParam) { CChatRoomDlg *pChatRoom = (CChatRoomDlg *)pParam;//创建窗口指针 ASSERT(pChatRoom != NULL);//判断线程是否创建成功 pChatRoom->m_ListenSock = socket(AF_INET , SOCK_STREAM , IPPROTO_TCP);//创 //建socket并用一个变量保存 if ( pChatRoom->m_ListenSock == INVALID_SOCKET ) {//如果创建的socket是无效套接字 AfxMessageBox(_T(\新建Socket失败!\ return FALSE; }
int iPort = pChatRoom->GetDlgItemInt(IDC_LISTEN_PORT); if ( iPort <= 0 || iPort > 65535 ) {
30
AfxMessageBox(_T(\请输入合适的端口:1 - 65535\ goto __Error_End; }
sockaddr_in service;//定义结构储存IP,端口号用于bind service.sin_family = AF_INET; service.sin_addr.s_addr = INADDR_ANY; service.sin_port = htons(iPort);
if ( bind(pChatRoom->m_ListenSock, (sockaddr*)&service, sizeof(sockaddr_in)) == SOCKET_ERROR ) {//判断绑定端口是否成功 AfxMessageBox(_T(\绑定端口失败!\ goto __Error_End; }
if( listen(pChatRoom->m_ListenSock, 5) == SOCKET_ERROR ) {//判断监听端口是否成功 AfxMessageBox(_T(\监听失败!\ goto __Error_End; } pChatRoom->ShowMsg(_T(\系统信息:启动服务器成功!\ pChatRoom->m_bIsServer = TRUE;//用于判断后面发送消息的是服务端 pChatRoom->EnableWindow(IDC_START_SERVER, FALSE);//隐藏窗口 pChatRoom->EnableWindow(IDC_STOP_SERVER);
while( TRUE && !(pChatRoom->bShutDown)) {//为了连接多个客户端 if ( SOCKET_Select(pChatRoom->m_ListenSock, 100, TRUE) ) {//查看缓冲区中是否 //有连接的客户端 sockaddr_in clientAddr;//定义变量供accept使用 int iLen = sizeof(sockaddr_in); SOCKET accSock = accept(pChatRoom->m_ListenSock, (struct sockaddr *)&clientAddr , &iLen);//连接缓冲区中客户端 if (accSock == INVALID_SOCKET) { continue; } CClientItem tItem;//定义客户端节点 tItem.m_Socket = accSock; tItem.m_pMainWnd = pChatRoom; tItem.m_strIp = inet_ntoa(clientAddr.sin_addr);//转换IP地址格式 INT_PTR idx = pChatRoom->m_ClientArray.Add(tItem);//将新连接的客户端入队 tItem.hThread=CreateThread(NULL,0,ClientThreadProc,
&(pChatRoom->m_ClientArray.GetAt(idx)), CREATE_SUSPENDED, NULL);//给连入客户端 //创建线程,客户端并发运行 pChatRoom->m_ClientArray.GetAt(idx).hThread = tItem.hThread;//保存线程,方 //便之后的其他使用 ResumeThread(tItem.hThread);//恢复线程运行 CString strMsg;
strMsg = _T(\客户端:\进入聊天室!\
pChatRoom->ShowMsg(strMsg);
31
pChatRoom->SendClientsMsg(strMsg, &tItem);//消息的转发 Sleep(100);
} }
__Error_End: closesocket(pChatRoom->m_ListenSock); return TRUE; }
DWORD WINAPI ClientThreadProc(LPVOID lpParameter)//客户端处理线程函数: { CString strMsg;
CClientItem m_ClientItem = *(CClientItem *)lpParameter;//复制传进来的结点避免以前结 //点被修
while( TRUE && !(m_ClientItem.m_pMainWnd->bShutDown)) {//一直监视连接进来的客户端 if ( SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE) ) {//判断缓冲区内是否有信息 TCHAR szBuf[MAX_BUF_SIZE] = {0};//定义一个缓冲区用于存储接收进来的信息 int iRet = recv(m_ClientItem.m_Socket, (char *)szBuf, MAX_BUF_SIZE, 0);//接收信息 if ( iRet > 0 ) {//接收成功 strMsg = szBuf; strMsg = _T(\客户端:\ m_ClientItem.m_pMainWnd->ShowMsg(strMsg); m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem); }else{//客户端关闭 strMsg = _T(\客户端:\离开了聊天室!\ m_ClientItem.m_pMainWnd->ShowMsg(strMsg); m_ClientItem.m_pMainWnd->RemoveClientFromArray(m_ClientItem); m_ClientItem.m_pMainWnd->SendClientsMsg(strMsg, &m_ClientItem); break; } } Sleep(500); } return TRUE; }
客户端类的设计: #pragma once
class CChatRoomDlg;
#define MAX_BUF_SIZE 1024
#define WM_TRAYICON_MSG (WM_USER+100)
class CClientItem {//客户端节点类用于保存IP,socket,handle和指向窗口的指针 public: CString m_strIp; SOCKET m_Socket; HANDLE hThread;
32
CChatRoomDlg *m_pMainWnd; CClientItem(){ m_pMainWnd = NULL; m_Socket = INVALID_SOCKET; hThread = NULL; } };
DWORD WINAPI ListenThreadFunc(LPVOID pParam); DWORD WINAPI ClientThreadProc(LPVOID lpParameter); DWORD WINAPI ConnectThreadFunc(LPVOID pParam);
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut = 100, BOOL bRead = FALSE);
33
指导教师评语: 成绩: 指导教师: 年 月 日
34
正在阅读:
局域网聊天程序设计10-01
陈用胜解析2018北京高考化学试题09-20
它让我欢笑让我忧作文350字07-12
2012年科学中考物理专题专练:压力和压强(答案)10-22
吉林大学研究生答辩要求及论文书写格式03-11
大三副通杀航海英语60011-17
学会理解作文500字04-01
浅谈幼儿园面向国际化09-16
中国船舶工业集团公司08-30
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 程序设计
- 局域网
- 聊天
- 按摩椅营销策划
- 浅谈现代监狱建筑设计
- 《小英雄雨来》教学设计(公开课)
- 2017-2022年中国小麦胚芽油行业市场监测与投资方向研究报告(目录)
- 《现代汉语》黄廖版课后习题上下册完整版答案
- 11-2 第四模块 Powerpoint幻灯片制作 动画设置与切换 首页
- 2018-2019年小学六年级上册数学第三次月考试卷有答案
- xxx市人民政府总值班室工作制度
- 同义词库
- 08美国数学建模比赛A题
- 将来时理论及练习
- 红色经典读后感征文比赛实施方案文档
- 论合作社:讲义
- 气质概述
- 校本课程工作总结
- 2018年中国汽车美容市场分析报告-行业深度分析与投资前景研究(目录)
- 围绕核心概念,建构科学概念 - 《动物的卵》一课科学概念建构初探
- 2019-2025年中国女装行业现状分析与发展前景研究报告目录
- 煤炭协会新任会长演讲稿
- SAGE X3 V5系统安装 - 图文