C++面向对象毕业课程设计报告局域网聊天程序.docx
《C++面向对象毕业课程设计报告局域网聊天程序.docx》由会员分享,可在线阅读,更多相关《C++面向对象毕业课程设计报告局域网聊天程序.docx(22页珍藏版)》请在冰豆网上搜索。
C++面向对象毕业课程设计报告局域网聊天程序
XXXXX大学
C++面向对象课程设计报告
院(系):
计算机工程学院
专业:
计算机网络工程
学生姓名:
XXXXXX
班级:
_网络081__ 学号:
题目:
局域网聊天程序
起迄日期:
_2010.12.20–2010.12.31
设计地点:
XX理工大学2#401
指导教师:
XXXXXX
完成日期:
2010年12月31日
目录
一需求分析3
1.1客户机端3
1.2服务器端3
二设计内容4
2.1程序整体结构4
2.2各部分子模块功能5
2.3各子模块代码编写6
三调试分析8
3.1实际完成情况8
3.2出现的问题及解决方案8
3.3可以改进的地方9
3.4可以扩充的功能10
四用户手册11
4.1程序主界面11
4.2获取本地信息11
4.3发送信息12
4.4接收信息12
4.5总体演示12
五设计总结13
六参考文献14
七附录15
1需求分析
此次开发的网络聊天工具可作为个人的交流工具使用,通信的安全性不是很高,但要求信息的响应速度要较快,让用户充分享受到网络即时消息的方便和快捷。
本聊天工具由服务器端程序和客户端程序两部分组成,整体采用C++平台开发,没有使用管理数据库。
服务器和与客户端都包含与用户的交互式界面,它有必要的界面的按钮,向用户提供网络即时消息的功能。
本聊天需要包含如下本功能:
1.1客户机端
✓获取本地IP地址
✓获取本地通信端口
✓获取远程IP地址
✓获取本地主机名
✓获取从服务器端发送过来的信息
✓给服务器发送用户自定义的数据
1.2服务器端
✓获取本地IP地址
✓获取本地通信端口
✓获取远程IP地址
✓获取本地主机名
✓获取从服务器端发送过来的信息
✓向客户机发送用户自定义的数据
2设计内容
2.1程序整体结构
2.2各部分子模块功能
2.3各子模块代码编写
1)WM_INITDIALOG模块
switch(UMsg)
{
caseWM_INITDIALOG:
CLIENT.INIT_MYSOCKET(hwndDlg);
===========================================
voidMYSOCKET:
:
INIT_MYSOCKET(HWND&hwndDlg)
{WSAStartup(0x0101,&wsaData);
udp_sd=socket(AF_INET,SOCK_DGRAM,0);
bind(udp_sd,(LPSOCKADDR)&Local_PC,sizeof(Local_PC));
WSAAsyncSelect(udp_sd,hwndDlg,WM_USER+1,FD_READ);
}
2)IDOK模块
switch(LOWORD(wParam))
{
caseIDOK:
CLIENT.MYSOCK_SEND(hwndDlg);
break;
===========================================
voidMYSOCKET:
:
MYSOCK_SEND(HWND&hwndDlg)
{GetDlgItemText(hwndDlg,IDC_SEND,Buffer,sizeof(Buffer));
sendto(udp_sd,Buffer,strlen(Buffer),0,(structsockaddr*)&Remote_PC,len);
}
3)IDCANCEL模块
caseIDCANCEL:
CLIENT.END_MYSOCKET(hwndDlg);
EndDialog(hwndDlg,0);
break;
===========================================
voidEND_MYSOCKET(HWND&hwndDlg)
{WSAAsyncSelect(udp_sd,hwndDlg,0,0);
closesocket(udp_sd);
WSACleanup();
}
4)FD_READ模块
caseWM_USER+1:
switch(LOWORD(lParam))
{
caseFD_READ:
CLIENT.MYSOCK_RECV(hwndDlg);
break;
}
===========================================
voidMYSOCKET:
:
MYSOCK_RECV(HWND&hwndDlg)
{
recv(udp_sd,Buffer,sizeof(Buffer),0);
SetDlgItemText(hwndDlg,IDC_RECV,Buffer);
}
3调试分析
3.1实际完成情况
经过几天的学习及调试,该“局域网聊天程序”仅仅能实现点对点通信,不能实现一对多、多对多、多对一等通信模式,也就是说,只能开启一个客户机和一个服务器,若有多个程序副本在运行,则可能导致接收以及发送数据的混乱。
用户在输入数据的时候不能包含换行符号,即只能输入一行信息。
用户在接受数据的时候,不能判定它是来自哪台主机。
服务器端不能自动发现上线的客户端,同样客户端也不能自动发现上线的服务器端,两者都是在假设对方在线的前提下发送数据的,这是因为该“局域网聊天程序”是基于UDP协议编写的,它是无连接的协议。
3.2出现的问题及解决方案
1)C++类的封装性
封装就是把对象的属性和操作结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节。
通过对抽象结果进行封装,将一部分行为作为外部访问的接口与外部发送联系,而将数据和其他行为进行有效隐藏,就可以达到对数据访问权限的合理控制。
通过这种有效隐藏和合理控制,就可以增强数据的安全性,减轻开发软件系统的难度。
在类中,封装是通过存取权限实现的。
虽然封装性是C++的一个重要特点,但在编写“局域网聊天程序”的时候给我造成了很大的阻碍。
起初我是把负责“接收”、“发送”数据的缓冲变量“Buffer”封装在类中(私有成员),但操作Buffer的函数(如GetDlgItemText、SetDlgItemText)都是在类外访问Buffer变量,开始以为通过return语句把指针传送到对象外就能操作对象里面的Buffer变量,但经过一段时间的测试,程序不能正常运行。
所以我改变了类的封装方式,不但把Buffer变量封装在类里面,而且把操作Buffer变量的函数也封装为类中的某些成员函数,这样就解决了C++类的封装特性给我带来的问题。
2)对象是自动变量
局部变量,如不作专门说明为静态变量,都是动态分配存储空间,存储在动态存储区中。
这种变量叫自动变量。
这种变量只在定义它们的时候才创建,在定义它们的函数返回时系统回收变量所占存储空间。
对这些变量存储空间的分配和回收是由系统自动完成的,所以也叫自动变量。
一般情况下,不作专门说明的局部变量,均是自动变量。
BOOLDialogProc(HWNDhwndDlg,UINTUMsg,WPARAMwParsam,LPARAMlParam)
{
staticMYSOCKETCLIENT(TEXT("127.0.0.1"),4321,TEXT("127.0.0.1"),6321);
switch(UMsg)
{
caseWM_INITDIALOG:
CLIENT.INIT_MYSOCKET(hwndDlg);//初始化套接字
……………………………….
DialogProc函数是系统指定的对话框消息处理回调函数,它是被系统自动地根据用户消息调用。
而且发送给该函数的消息“WM_INITDIALOG”只有在初始化对话框时才被调用,由于局部变量默认是自动变量,所以由MYSOCKET类构造的CLIENT对象在每次系统回调该函数的时候,都会被初始化一遍,而这种操作破坏了套接字与本地信息的绑定,因此服务器端于客户机端程序不能正常通信。
解决这个问题的方法就是使用static操作符使变量变成静态值,使CLIENT对象在函数在多次被调用时,能够维持它的原始值。
即把语句
MYSOCKETCLIENT(TEXT("127.0.0.1"),4321,TEXT("127.0.0.1"),6321);
改变为语句
staticMYSOCKETCLIENT(TEXT("127.0.0.1"),4321,TEXT("127.0.0.1"),6321);
程序正常运行
3)端口冲突
程序给服务器端分配的的端口号是5000,给客户机端分配的端口号是4000。
程序一直运行的很正常,但偶然的一次测试发现程序间不能正常通信,经过反复的调试、测试并没有发现代码中存在什么问题。
我试着改了改端口号服务器端:
6321;客户机端:
4321。
测试通过了。
3.3可以改进的地方
1)用户界面
用VC++开发的界面生硬、死板,并不友好,若能使用一些开发程序皮肤的库文件,使用户界面更加友好。
3.4可以扩充的功能
1)多用户聊天
本程序的逻辑拓扑可以从基于UDP无连接协议的“点对点”拓扑,转化为基于TCP面向连接的“星型”拓扑,以实现多用户之间的回话。
若能采用TCP协议,用户间的通信时通过服务器的转发功能实现的,服务器端程序不修改接收的的信息,只充当“数据转发角色”,把数据按照用户想要传送的目的地发送到目的。
假设客户机A想要把数据发送给客户机B,客户机A先把数据发送给服务器端,然后服务器将数据转发给客户机B,这样对客户机A而言,服务器端是透明的,好像是客户机直接在跟客户机B通信一样。
具体的拓扑如下图所示:
2)自动发现功能
假设服务器端程序在线,当客户端程序上线的时候,服务器端能够自动侦测到客户机端上线,并获取客户机端的用户名、IP地址、通信端口号等必要通信信息,并记录在自己的数据库中登记。
3)文件发送共更能
在聊天功能(即发送字符信息)成功实现的前提下,可以再加入文件传送功能,使这个“局域网聊天程序”的功能更加强大。
4用户手册
4.1程序主界面
虽然本程序是基于客户机/服务器(C/S)模式的通信,但两者的用户界面是一致的,所以在使用上不会给用户带来困难。
服务器端、客户端都包含一个接受信息的控件(IDC_RECV)、一个用于发送信息的控件(IDC_SEND)、一个发送按钮(IDOK)、部分静态文本(IDC_STATIC)、部分本机信息控件(本地主机名(IDC_HOST)、本地IP地址(IDC_IP)、本地端口号(IDC_PORT)、远程IP地址(IDC_IP_REMOTE))。
4.2获取本地信息
服务器端和客户机端的信息是自动获取的,不需要用户干预。
它将自动获取本地主机名、本地IP地址、本地端口号、远程通信IP地址。
下面是在不同计算机上运行客户机端的本地信息截图。
4.3发送信息
只要在“发送:
”下面的控件窗口中填入您想要发送的信息,然后按“发送”按钮,即可发送到远程计算机。
4.4接收信息
客户端、服务器端接收的信息,都会显示在“接收”控件中。
4.5总体演示
1.同时运行UDPClient和UDPServer。
2.在Server端发送“Hello,I’MServer!
”,在Client端发送“Hello,IMClient”。
3.在程序、网络正常的情况下,Server端会接收到Client端发送的信息“Hello,IMClient”;同样,Client端会接收到Server端发送的信息“Hello,I’MServer!
”。
5设计总结
经过两个两个星期的实践学习,使我对C++语言、WINDOWS编程、WINDOWSSOCKET编程有了更进一步的认识和了解,要想学好它重要在实践,要通过不断的上机操作才能更好地学习它,通过实践,我也发现我的好多不足之处,首先是自己在指法上还不行,经常按错字母,通过学习也有所改进;再有对SOCKETAPI中的一些标准函数库不太了解,还有对函数调用的正确使用不够熟悉,还有对C++语言中经常出现的错误也不了解,通过实践,使我在这几个方面的认识有所提高。
通过实践的学习,我认识到学好计算机要重视实践操作,不仅仅是学习C++语言,还是WINDOWS编程,以及其它的计算机方面的知识都要重在实践,所以以后再学习过程中,我会更加注重实践操作,使自己更好地学好计算机。
尤其是在使用SDK编写基于对话框的程序时,接触了全新的WINDOWS的“消息处理”机制。
在参考了众多资料后,成功调试了“局域网聊天程序”的用户界面,这为后期编写SOCKET核心代码提供了基础。
编写通信部分核心代码时,也加深了对“套接字”、“绑定”、“动态链接库”、“端口”等属于的理解。
由于本次设计接触到了两个全新的领域,一是网络通信程序的编写,另一个是C++面向对象程序的编写,为了在开发后期更好的分离错误的出处,我先是用C语言独立编写了网络通信部分、用户界面部分,然后再由C++将其封装为类,但中间的调试花费了我大量的时间,就是因为没有充分理解C++类的封装性与C面向过程程序设计的异同。
所以本次开发使我更好的认识了C++、C、Windows以及网络通信的知识。
通过分析、设计、编码、调试等各环节的训练,深刻理解、C++程序设计技术,掌握分析、解决实际问题的能力。
综合运用所学知识,上机解决一些与实际应用结合紧密的、规模较大的问题,逐步掌握软件开发的基本思想、方法和实现步骤,提高实际应用水平。
这次课程设计基本上含盖了本学期学习到的C++语言知识点,课设题目要求不仅要求对课本知识有较深刻的了解,同时要求程序设计者有较强的动手能力以及自学能力。
这次课设使我了解我编程思想和编程技巧,也认识了软件生命周期的各个环境,包括构思、设计、编写、调试、发布、文档化、维护和修订。
编程的风格也很重要,程序有了良好的编程风格,有良好的程序注释,会在后期调试程序、程序排错过程中获益的;若只关心程序运行的结果,而对程序代码的结构的良好丝毫不在意,是非常不可取的,如果我们希望将来从事编程工作,在这一点上该引起足够的重视。
这是严谨的态度,很重要!
6参考文献
1.JeffreyRichter,ChristopheNasarre.Windows核心编程(第五版).北京:
清华大学出版社.2008.9
2.CharlesPetzold.Windows程序设计(第五版).北京:
北京大学出版社.1999.11
3.KennethA.Reek.C和指针。
北京:
人名邮电出版社.2008.4
4.李兰,任凤华.C++面向对象程序设计.西安:
西安电子科技大学出版社.2010.9
5.W.RichardStevens.TCP/IP详解卷1:
协议.北京:
机械工业出版社.2000.4
6.郑莉.C++语言程序设计(第3版).北京:
清华大学出版社,2005.7
7.钱能.C++程序设计教程(第2版).北京:
清华大学出版社,2005.8
8.谭浩强.C++程序设计.北京:
清华大学出版社.2001
7附录
/////////////////////////////////////////////////////////////////////////
//服务器端与客户机端所使用的类
//COMMON.H
classMYSOCKET
{
public:
/*
**MYSOCKET类构造函数
**1.保存通信端口到成员变量
**A.本地:
Local_Port_Num
**B.远程:
Remote_Port_Num
**
**2.设置通信地址及端口
**A.本地:
127.0.0.1:
local_port
**B.远程:
127.0.0.1:
remote_port
**
**3.获取"structsockaddr"类型长度
*/
MYSOCKET(TCHAR*Local_IP,
WORDlocal_port,
TCHAR*Remote_IP,
WORDremote_port)
{
Local_Port_Num=local_port;
Remote_Port_Num=remote_port;
memset(&Local_PC,0,sizeof(Local_PC));
Local_PC.sin_family=AF_INET;
Local_PC.sin_addr.s_addr=inet_addr(Local_IP);
Local_PC.sin_port=htons(local_port);
memset(&Remote_PC,0,sizeof(Remote_PC));
Remote_PC.sin_family=AF_INET;
Remote_PC.sin_addr.s_addr=inet_addr(Remote_IP);
Remote_PC.sin_port=htons(remote_port);
len=sizeof(structsockaddr_in);
}
/*
**初始化套接字
**1.用WSAStartup函数启动网络动态链接库
**2.用socket函数生成UDP套接字
**3.用bind函数将UDP套接字与本机绑定
**4.用WSAAsyncSelect函数注册网络异步选择事件消息
*/
voidINIT_MYSOCKET(HWND&hwndDlg)
{
WSAStartup(0x0101,&wsaData);
udp_sd=socket(AF_INET,SOCK_DGRAM,0);
bind(udp_sd,(LPSOCKADDR)&Local_PC,sizeof(Local_PC));
WSAAsyncSelect(udp_sd,hwndDlg,WM_USER+1,FD_READ);
}
/*
**发送消息
**1.用GetDlgItemText函数从控件IDC_SEND获取用户即将发送的数据
**2.用sendto函数向目的主机发送UDP数据报
*/
voidMYSOCK_SEND(HWND&hwndDlg)
{
GetDlgItemText(hwndDlg,IDC_SEND,Buffer,sizeof(Buffer));
sendto(udp_sd,Buffer,strlen(Buffer),0,(structsockaddr*)&Remote_PC,len);
}
/*
**接收消息
**1.用recv函数接受数据,并存储于Buffer缓冲区
**2.用SetDlgItemText函数设置控件IDC_RECV,使信息显示在屏幕上
*/
voidMYSOCK_RECV(HWND&hwndDlg)
{
recv(udp_sd,Buffer,sizeof(Buffer),0);
SetDlgItemText(hwndDlg,IDC_RECV,Buffer);
}
/*
**设定本地及远程主机IP
*/
voidMYSOCK_SET_REMOTE(constTCHAR*remote_IP)
{
//功能还未实现
}
/*
**获取本地主机名
**1.将主机名保存于成员变量HOSTNAME中
**2.用wsprintf函数将主机名传送到形参中
*/
voidMYSOCK_GET_HOSTNAME(TCHAR*HOST_BUFFER)
{
gethostname(HOSTNAME,sizeof(HOSTNAME));
wsprintf(HOST_BUFFER,"%s",HOSTNAME);
}
/*
**获取本地通信端口(字符串格式)
**直接用wsprintf函数转换
*/
voidMYSOCKET_GET_PORTNUM(TCHAR*PORT_BUFFER)
{
wsprintf(PORT_BUFFER,"%d",Local_Port_Num);
}
/*
**获取本地通信地址(字符串格式)
**1.用gethostbyname函数取得相关信息
**2.用inet_ntoa函数将网络地址转换为点分字符串形式
**3.用wsprintf函数转换格式并输出
*/
voidMYSOCKET_GET_LOCALIP(TCHAR*IP_BUFFER)
{
HOSTENT=gethostbyname(HOSTNAME);
wsprintf(IP_BUFFER,"%s",inet_ntoa(*(structin_addr*)HOSTENT->h_addr_list[0]));
}
/*
**结束套接字过程
**1.注销网络异步选择事件消息。
**2.关闭套接口
**3.卸载网络动态链接库
*/
voidEND_MYSOCKET(HWND&hwndDlg)
{
WSAAsyncSelect(udp_sd,hwndDlg,0,0);
closesocket(udp_sd);
WSACleanup();
}
private:
/*
**WSADATA以及SOCKET变量
**相关函数:
MYSOCKET:
:
INIT_MYSOCKET(HWND&hwndDlg)
*/
WSADATAwsaData;
SOCKETudp_sd;
/*
**1.Local_Port_Num==>本地端口号
**Remote_Port_Num==>目的端口号
**
**2.Local_PC==>本地通信结构
**Remote_PC==>远程通信结构
**
**3.len==>"structsockaddr"类型的长度
**
**相关函数:
MYSOCKET:
:
MYSOCKET(WORDlocal_port,WORDremote_port)
*/
WORDLocal_Port_Num,Remote_Port_Num;
structsockaddr_inLocal_PC,Remote_PC;
intlen;
/*
**数据接收、发送缓冲区
**相关函数:
MYSOCKET:
:
MYSOCK_RECV(HWND&hwndDlg)
**MYSOCKET:
:
MYSOCK_SEND(HWND&hwndDlg)
**
*/
TCHARBuffer[100];
/*
**保存本地主机名
**相关函数:
MYSO