青岛理工大学C++面向对象课程设计报告院(系):计算机工程学院专业:计算机科学与技术学生姓名:张义华班级:__113___学号:201107104题目:局域网聊天程序起迄日期:_2013.6.23~2013.7.5设计地点:计算机学院机房指导教师:李兰完成日期: 2013 年7月 5目录一、需求分析-----------------------------------------------------------------------------------------------31.1 客户端1.2 服务端二、设计内容-----------------------------------------------------------------------------------------------42.1程序整体构建逻辑图2.2 基于UDP的客户端和服务端的连接逻辑图2.3各模块代码实现三、调试分析-----------------------------------------------------------------------------------------------103.1实际完成的情况说明3.2程序的性能分析和上机过程中出现的问题及其解决方案3.3程序中可以改进的地方说明四、用户手册-----------------------------------------------------------------------------------------------11五、设计总结-----------------------------------------------------------------------------------------------13六、参考文献-----------------------------------------------------------------------------------------------13七、附录-----------------------------------------------------------------------------------------------------14一、需求分析这次开发的局域网聊天工具目标在于在局域网环境下能够简单的实现人与人的即时通讯,要求收发信息速度快、安全性高和精准性高。
本软件基于visual c++ 6.0环境下开发,并采用MFC进行处理,界面具有基本的功能按钮,于此本人还添加了一个查询本地的主机名和IP地址的功能。
同时,本软件包含有客户端和服务端,他们之间是基于UDP协议下进行通信的,他们具备的功能如下:1.1客户端①点击查询按钮获取本地主机名和IP②获取远程服务端端口③输入远程服务端IP并连接④用户能够自定义编辑发送信息并点击发送按钮发送⑤用户能够接收到来自远程服务端的信息⑥用户右键点击窗口可以查询软件版本⑦自动建立一个文件存储聊天记录1.2服务端①点击查询按钮获取本地主机名和IP②获取远程客户端端口③输入远程客户端IP并连接④用户能够自定义编辑发送信息并点击发送按钮发送⑤用户能够接收到来自远程客户端的信息⑥用户右键点击窗口可以查询软件版本⑦自动建立一个文件存储聊天记录二、设计内容2.1程序整体构建逻辑图:2.2 基于UDP 的客户端和服务端的连接逻辑图基于UDP 的套接字编程,对于接收端(服务端)来说,它必须先启动以接收客户端发送的数据,因此接收端必须告诉主机它是在哪个地址和端口上等待数据的到来,也就是说,接收端(服务端)必须调用bind ()函数将套接字绑定到一个本地地址和端口上。
服务端客户端2.3各模块代码实现1)、//初始化套接字BOOL CCHATDlg::InitSocket(){m_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(m_socket==INV ALID_SOCKET){MessageBox("套接字创建失败!");return FALSE;}SOCKADDR_IN ServerAddr;ServerAddr.sin_family=AF_INET;ServerAddr.sin_port= htons(6000);ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);int retval;retval=bind(m_socket,(SOCKADDR*)&ServerAddr,sizeof(SOCKADDR));if(retval==SOCKET_ERROR){closesocket(m_socket);MessageBox("绑定失败!");return FALSE;}return TRUE;}2)、//消息处理函数,注意在声明时应该为staticDWORD WINAPI CCHATDlg::RecvProc(LPVOID lpParameter){SOCKET sock=((RECVPARAM*)lpParameter)->sock;HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;SOCKADDR_IN ClientAddr;int len=sizeof(SOCKADDR);char recvBuf[512];char tempBuf[512];int retval;while(1){retval=recvfrom(sock,recvBuf,512,0,(SOCKADDR*)&ClientAddr,&len);if(retval==SOCKET_ERROR)break;sprintf(tempBuf,"(%s): %s",inet_ntoa(ClientAddr.sin_addr),recvBuf);::PostMessage(hwnd,WM_RECVDA TA,0,(LPARAM)tempBuf);}return 0;}3)、同时,在BOOL CCHATDlg::OnInitDialog()添加如下代码:InitSocket(); //套接字初始化RECVPARAM *pRecvParam=new RECVPARAM;pRecvParam->sock=m_socket;pRecvParam->hwnd=m_hWnd;HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);CloseHandle(hThread);4)、定义一个全局静态变量CString NewString 用于进行格式输出和记录聊天消息//发送数据void CCHATDlg::OnBtnSend(){//TODO: Add your control notification handler code herDWORD dwIP;((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);SOCKADDR_IN ClientAddr;ClientAddr.sin_family=AF_INET;ClientAddr.sin_port= htons(5150);ClientAddr.sin_addr.S_un.S_addr = htonl(dwIP);CString strSend;CString strTime;GetDlgItemText(IDC_EDIT_SEND,strSend);if(strSend.GetLength()==0){MessageBox("发送的消息不能为空!");}else{sendto(m_socket,strSend,strSend.GetLength()+1,0,(SOCKADDR*)&ClientAddr,sizeof(SOC KADDR));SetDlgItemText(IDC_EDIT_SEND,"");//聊天消息时间记录CTime tm;tm=CTime::GetCurrentTime();strTime=tm.Format("%Y-%m-%d %X");strSend=strSend+" 【";strSend+=strTime;strSend=strSend+"】";strSend+="\r\n";NewString+="\r\n";NewString+="我: ";NewString+=strSend;Record(NewString); //调用消息记录函数SetDlgItemText(IDC_EDIT_RECV,NewString); //回显在本地聊天窗口}}5)、//接收数据void CCHATDlg::OnRecvData(WPARAM wParam,LPARAM lParam){CString str=(char*)lParam;CString strTime;GetDlgItemText(IDC_EDIT_RECV,NewString);//聊天消息时间记录CTime tm;tm=CTime::GetCurrentTime();strTime=tm.Format("%Y-%m-%d %X");str=str+" 【";str+=strTime;str=str+"】";str+="\r\n";NewString+="\r\n";NewString+=str;SetDlgItemText(IDC_EDIT_RECV,NewString);Record(NewString); //调用消息记录函数}6)、附加查询本地主机名和IP功能void CCHATDlg::OnBUTTONsearch() //查询按钮{WSADATA wsaData;WSAStartup(MAKEWORD(1,1),&wsaData);char szHostName[128];if(gethostname(szHostName,128)==0) //获取主机名{m_HostName.SetWindowText(szHostName);hostent* ent=gethostbyname(szHostName); //获取主机信息for(int i=0;ent!=NULL && ent->h_addr_list[i]!=NULL;i++){LPCTSTR IpAddr=inet_ntoa(*(in_addr*)ent->h_addr_list[i]); //转换成点分十进制格式m_IPAddr.AddString(IpAddr);}}WSACleanup();}7)、聊天记录int CCHATDlg::Record(CString recordString) //消息记录函数{CFile recordFile("TheRecords.txt",CFile::modeCreate|CFile::modeWrite); //定义文件对象recordFile.Write(recordString,strlen(recordString));recordFile.Close();return 0;}8)、在CHATDlg.h文件添加宏:#define WM_RECVDATA WM_USER+1三、调试分析1、实际完成的情况说明:本软件目前完成了基本功能需求,服务端和客户端可以进行简单的文本消息通信,两端皆可查询本地的主机名和IP,同时可以存储聊天记录。