您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 广告经营 > 程序设计报告之简易聊天软件的设计与实现
-程序设计报告(2012/2013学年第二学期)题目:简易聊天软件的设计与实现专业学生姓名班级学号指导教师指导单位计算机学院计算机科学与技术系日期2013年3月28日-评分细则评分项优秀良好中等差遵守机房规章制度上机时的表现学习态度程序准备情况程序设计能力团队合作精神课题功能实现情况算法设计合理性用户界面设计报告书写认真程度内容详实程度文字表达熟练程度回答问题准确度简短评语教师签名:2013年3月29日评分等级备注评分等级有五种:优秀、良好、中等、及格、不及格-一、课题名称简易聊天软件的设计与实现二、课题内容和要求1、课题内容:本课程设计主要是设计并实现一个简单的聊天程序,创建基于多线程的聊天室程序。2、课题要求:要求能够实现基本的聊天功能,本聊天室允许两台计算机之间进行聊天,但是需要获取进行聊天的两台机之间IP地址,可以实现一对一的聊天。三、原理分析1、该程序实现局域网内的聊天功能,包括服务器端程序和客户端程序两部分。客户端程序:可连接到服务器,并将消息发送到服务器端和接受服务器端发送到的消息。服务器端程序:负责发送用户列表和转发客户端发送过来的消息。本程序涉及到服务器端和客户端,采用同一个套接字。2、该聊天软件是采用UDP连接,UDP是OSI参考模型中一种无连接的传输层协议,它提供了简单不可靠的信息传送服务。由于UDP比较简单,UDP包含很少的字节,所以它在速度方面有很大优势。很多常用的即时通软件,如QQ程序,都会使用UDP实现很多基本功能。UDP是“面向非连接”的网络协议,它与“面向连接”的TCP协议对应。在发送数据之前,并不与对方建立连接,而是直接把数据报发出去,不保证可靠的传输。UDP相对TCP简单,在速度方面有很大优势,因为它的网络开销少,对传输可靠性要求不是很高的情况下,UDP的使用是网络程序的首选。3、要实现聊天功能必须获取两台计算机的IP地。在IP地址控件栏输入参与聊天对象的计算机的IP地址。4、本程序的核心在于将消息的发送的和接收发在了两个不同的线程中,接收放在新创建的副进程中,因为其要一直处于响应状态,也就是需要一个while循环;发送放在主线程中。这样消息的接收和发送就不存在先后顺序了,且一直处于循环中的接收也不会影响到发送。5、程序代码中的新线程入口函数中可能没有必要传递两个参数进去,其中SOCKET参数可以在入口函数内部创建,SOCKET变量也就是声明是TCP还是UDP,和发送或接收没有必-然的联系,如果这样的话,就没有必要声明“详细设计”第五步中的结构体了,CreateThread方法也刚好传递一个参数,即当前窗口的句柄。四、需求分析1、在VC++6.0中MFC中创建新文档,选中基本对话框栏,然后进行对话框的设置,选择不同的控件,分别设置接收数据、发送数据和发送的控件。2、对需要用的变量进行定义并初始化等。3、实现不同的功能响应不同的消息处理函数,实现套接字绑定获取IP地址等功能。4、理解CWinApp中的InitInstance函数的用法及功能。5、WSACleanup函数的调用与终止等。6、各种不同代码的功能与实现原理。五、概要设计1、对需要用的变量进行定义或申明。2、调用相应的MFC内置函数,对相应的变量进行初始化等操作。3、程序设计的概要流程图如下:开始编辑界面添加套接字库头文件,调用WSACleanup,包含头文件Afxsock.h调用MFC的内置函数AfxSocketInit,-图1程序设计流程图六、详细设计1、创建一个基于对话框的MFC程序设计,界面如下:加载套接字库创建套接字在CDialog中添加成员变量:SOCKETm_socket,和自定义函数:BOOLCChatDlg::InitSocket(),在CChatDlg类添加代码分别完成服务器的创建和客户端消息的传送定义自定义消息的宏:#defineWM_RECVDATAWM_USER+1,完成消息的接收和显示部分在发送按钮点击的响应函数中添加相应函数代码结束-图2对话框界面2、添加套接字库头文件:函数能准确保证程序终止前调用WSACleanup的调用,该函数其实也是调用Win32中的WSAStartup,该函数的调用位置最好在CWinApp中的InitInstance中,包含头文件Afxsock.h,在StdAfx.h这个头文件中调用MFC的内置函数AfxSocketInit,该函数其他也是调用Win32中的WSASAtartup,该进行包含。StdAfx.h头文件是一个预编译头文件,在该文件中包含了MFC程序运行的一些必要的头文件,如afxwin.h这样的MFC核心头文件等。一些必要的头文件,如afxwin.h这样的MFC核心头文件等。它是一个被程序加载的文件。3、加载套接字库:在CWinApp中的InitInstance添加如下代码:if(FALSE==AfxSocketInit()){AfxMessageBox(套接字库加载失败!);returnFALSE;}4、创建套接字:将自己假想成服务器端,进行套接字和地址结构的绑定,等待别人发送消息过来。-在CDialog中添加成员变量:SOCKETm_socket添加自定义函数:BOOLCChatDlg::InitSocket(){m_socket=socket(AF_INET,SOCK_DGRAM,0);//UDP连接方式if(INVALID_SOCKET==m_socket){MessageBox(套接字创建失败!);returnFALSE;}SOCKADDR_INaddrServer;//将自己假想成serveraddrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);addrServer.sin_family=AF_INET;addrServer.sin_port=htons(1234);intretVal;retVal=bind(addrSock,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));if(SOCKET_ERROR==retVal){closesocket(addrSock);MessageBox(套接字绑定失败!);returnFALSE;}returnTRUE;}5、在CChatDlg类的外部添加结构体:structRECVPARAM{SOCKETsock;//保存最初创建的套接字HWNDhWnd;//保存对话框的窗口句柄-};6、在对话框的初始化代码中完成线程的创建:在CChatDlg::OnInitDialog函数中添加下面的代码:if(!InitSocket())//服务器端的创建returnFALSE;RECVPARAM*pRecvParam=newRECVPARAM;pRecvParam-hWnd=m_hWnd;pRecvParam-sock=m_socket;说明:1)接收部分应该一直处于响应状态,如果和发送部分放在同一段代码中,势必会阻塞掉发送功能的实现,所以考虑将接收放在单独的线程中,使它在一个while循环中,始终处于响应状态2)因为需要传递两个参数进去,一个是recvfrom需要用的套接字,另一个是当收到数据后需要将数据显示在窗口中的对应文本框控件上,所以需要传递当前窗口的句柄,但CreateThread方法只能传递一个参数,即第四个参数,这时候就想到了采用结构体的方式传递。HANDLEhThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);CloseHandle(hThread);7、创建线程入口函数RecvProc:如果是成员函数的话,那它属于某个具体的对象,那么在调用它的时候势必要让程序创建一个对象,但该对象的构造函数有参数的话,系统就不知所措了,所以可以将函数创建为全局函数,即不属于类,但这失去了类的封装性,最好的方法是将该方法声明为静态方法,它不属于任何一个对象。在CChatDlg类的头文件中添加:staticDWORDWINAPIRecvProc(LPVOIDlpParameter);在cpp文件中添加:DWORDWINAPICChatDlg::RecvProc(LPVOIDlpParameter){-RECVPARAM*pRecvParam=(RECVPARAM*)lpParameter;HWNDhWnd=pRecvParam-hWnd;SOCKETsock=pRecvParam-sock;charrecvBuf[200];charresultBuf[200];SOCKADDR_INaddrFrom;//这个时候是假想成服务器端intlen=sizeof(SOCKADDR_IN);while(TRUE)//处于持续响应状态{intretVal=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);if(SOCKET_ERROR==retVal){AfxMessageBox(接收数据出错);break;}else{sprintf(resultBuf,%ssaid:%s,inet_ntoa(addFrom.sin_addr),recvBuf);//现在已经拿到客户端送过来的消息了,但因为自身是静态函数,所以拿不到当前窗口对象中的控件的句柄,也就不能对其赋值了,唯一办法就是用消息的形式将接收到的值抛出到窗口的消息队列中,等待消息处理::PostMessage(hWnd,WM_RECVDATA,0,(LPARAM)resultBuf);}}return0;}8、自定义消息:定义自定义消息的宏:-#defineWM_RECVDATAWM_USER+1声明消息响应函数:因为有参数要传递,所以wParam和lParam都要写,如果没有要传递,可以不写afx_msgvoidOnRecvData(WPARAMwParam,LPARAMlParam);消息映射:ON_MESSAGE(WM_RECVDATA,OnRecvData)定义消息响应函数:voidCChatDlg::OnRecvData(WPARAMwParam,LPARAMlParam){CStringrecvData=(char*)lParam;CStringtemp;GetDlgItemText(IDC_EDIT_RECV,temp);temp+=\r\n;temp+=recvData;SetDlgItemText(IDC_EDIT_RECV,temp);}自此,消息的接收和显示部分已经完成了9、消息的发送:在发送按钮点击的响应函数中添加:DWORDdword;CIPAddressCtrl*pIPAddr=(CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1);pIPAddr-GetAddress(dword);//因为对方有具体的IP地址值,我们假想对方是服务器端。在发送的时候程序就从服务器的角色转变为客户端了SOCKADDR_INaddrServer;addrServer.sin_addr.S_un.S_addr=htonl(dword);addrServer.sin_family=AF_INET;addrServer.sin_port=htons(1234);CStringstrSend;GetDlgItemText(IDC_EDIT_SEND,strSend);-sendto(m_socket,strSend,strlen(strSend)+1,0,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));SetDlgItemText(IDC_EDIT_SEND,);10、消息面版的控制:至此,整个聊天软件的程序设计已经完成,但当你运行的时候发现两次发送的消息会在同一行出现。如图所示:此时,需要鼠标选中接收数据中的编辑一栏,更改其属性,选中属性中的样式然后选中多行。整个聊天软件的是设计与实
本文标题:程序设计报告之简易聊天软件的设计与实现
链接地址:https://www.777doc.com/doc-2238119 .html