用VC++简单的多线程聊天室程序完整课程设计Word文档下载推荐.docx
《用VC++简单的多线程聊天室程序完整课程设计Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《用VC++简单的多线程聊天室程序完整课程设计Word文档下载推荐.docx(26页珍藏版)》请在冰豆网上搜索。
1引言3
1.1课题背景及意义3
1.2实验平台介绍3
1.3可行性分析3
2需求分析5
2.1设计目的5
2.2设计要求5
2.3功能要求5
2.4系统主要功能和主要功能描述5
3设计流程图7
4调试分析过程描述9
5核心代码16
5.1服务端16
5.2客服端21
6设计的总结和体会24
7参考文献24
摘要
计算机网络技术发展至今已经大大超越了人们当初的预想,无论是人们日常的工作还是学习,我们都越来越多的依靠到互联网。
各种实时性的聊天娱乐软件也同时诞生,而且为我们的即时通讯带来了众多的方便,比如说大家所熟知的腾讯QQ、微软的MSN、移动的Fetion等,都是做的比较成功的实时聊天工具。
随着网络的日益普及,各种聊天工具也层出不穷,但当我们学习了《windows程序设计》这门课程之后,我们决定设计一个简单的聊天系统来巩固我们的学习。
接下来的课程设计就是针对一个简单的网络聊天程序,利用MFC为开发工具,实现基本的通讯功能。
在课程设计中,系统开发平台为WindowsXP,程序设计设计语言采用VisualC++,数据库采用Access,程序运行平台为Windows98/2000/XP。
关键词聊天软件;
局域网;
MFC;
VisualC++;
多线程
1引言
1.1课题背景及意义
当今世界正处于信息时代,计算机和通信网络是这一时代所谓“信息基础设施”。
在互联网相当普及的今天,在互联网上聊天对很多“网虫”来说已经是家常便饭了。
聊天室程序可以说是网上最简单的多点通信程序。
一个简单的聊天室,从程序员的观点来看就是在多个I/O端点之间实现多对多的通信。
基于SOCKET的局域网通信是一种灵活的、易于实现的、低成本的方法。
它可以运行在各种使用TCP/IP协议作为通讯协议的网络上。
而在SOCKETAPI的帮助下,开发基于SOCKET的局域网通信软件也是易于实现的。
1.2实验平台介绍
VisualC++(简称VC)是Microsoft公司推出的目前使用极为广泛的基于Windows平台的C++可视化开发环境。
VC基于C,C++语言,主要由是MFC组成,是与系统联系非常紧密的编程工具,它兼有高级,和低级语言的双重性,功能强大,灵活,执行效率高,几乎可说VC在
Windows平台无所不能。
VC主要是针对Windows系统,适合一些系统级的开发,可以方便实现一些底层
的调用。
在VC里边嵌入汇编语言很简单。
当对系统性能要求很高的时候,可用VC开发。
VC在多线程、网络通信、分布应用方面,有着不可比拟的优势。
1.3可行性分析
本课程设计主要解决在客户端于客户端的信息交换和客户端于服务器的信息交换及服务器的信息处理上的管理的课程设计。
此程序主要分为两部分:
服务器端和客户端。
服务器端用于提供一个网络端口,等待客户端发出请求,登录到此服务端,然后进行网络通讯和消息的转发;
客户端可通过服务器端的IP地址发送连接请求,然后登陆聊天室。
在服务器端的成员列表栏中会显示在线的所有人名单,有人退出聊天室,成员列表会自动除名。
服务器端同时也提供了成员之间的私聊功能,此时服务器端作为一个转发站,进行消息的转发。
整个程序的主体使用了CSocket类的方法,实现了网络通讯聊天。
先启动服务器端聊天程序,这是聊天服务器需要指定一个端口号,客户端则根据这个端口号以及服务器的网络地址与服务器进行通信。
在这里,把端口号成为“聊天频道”。
在后面的程序代码分析中将看到,端口号并不等同于聊天频道,而是在聊天频道上增加一个固定的偏移值,使得这个聊天频道不会和系统保留的端口发生冲突。
服务器启动后将在这个指定的端口号中等待客户的连接。
对于公共聊天室,服务器对客户的数目不做任何限制。
而对于私人聊天室,每个聊天频道则只能允许两个客户互相连接,使得一方发送的信息只能到达对方的主机中。
这里的服务器提供的是公共聊天服务。
通过分析发现,该程序完全可以通过VisualC++中MFC完成。
2需求分析
2.1设计目的
综合运用本课程及计算机网络的相关知识设计并实现一个网络应用程序,以VisualC++作为开发平台,通过实践复习巩固课堂所学的理论知识,提高对所学知识的综合应用能力。
2.2设计要求
采用客户/服务器模式,分为客户端程序和服务器端程序。
服务器采用WINSOCKI/O模型中的任一种,支持多个客户同时在线聊天。
客户端程序和服务器程序通过网络交换聊天字符串内容,服务器窗口的列表框中显示当前在线用户,支持客户端之间的私聊(可以通过服务器中转,或考虑UDP打洞直接建立端端连接)。
课程设计要求设计并编程完成两个方面的内容:
首先建立一个使用TCP协议的聊天室服务器,这个服务器可以同时支持多个用户的在线聊天;
其次设计一个可以和服务器通信的聊天室客户端。
2.3功能要求
✧支持多个客户端的连接,在服务器和多个客户端之间进行数据传输;
✧接收客户端发送的消息,并显示在一个列表框中;
✧在用户连接上后有提示,显示出连接的用户名字;
✧发送信息时可以显示聊天的所有记录;
2.4系统主要功能和主要功能描述
服务器端聊天程序必须能够做3件事情:
(1)服务器聊天程序要在待定的端口上等待来自聊天客户的连接请求,并且需要维护一个客户连接表,以记录所有成功的连接。
(2)服务器聊天程序要及时接受从各个聊天客户发送过来的信息,然后把这些信息转发到一个或多个客户连接。
对于公共聊天室,服务器将把接受到的信息向除源端外的所有客户发送过去。
(3)服务器还要监控这些连接的状态,在客户主动离开或发生故障时从列表中删除相应的表项,并及时更新连接表。
客户端聊天程序需要完成以下几个功能:
(1)客户端聊天程序要负责建立和维护与服务器的连接,通过获取用户的设置尝试与服务器的连接,并且随时检测连接的状态。
(2)客户端聊天程序要把用户输入的信息及时发送到聊天服务器。
一般情况下,当用户输入一行信息并且按下回车键后聊天程序就要把这一行信息发送出去,才能及时地满足用户的交互需求。
(3)要随时准备好接受来自服务器的信息,随时把接受到的信息显示出来,让用户及时看到对方的响应。
(4)在用户退出聊天过程是要关闭与服务器的连接。
比较好的做法是提前通知服务器或者直接给服务器发送一条退出通知,使得服务器能够及时掌握客户端的连接状态,把对方客户的退出信息及时发送到对等实体上。
3设计流程图
根据对用户的要求及功能设置可以得到以下的流程图3.1,用户首先启动客户端,登陆服务器并向服务器发送信息,启动服务器,服务器等待客户要求并向客户反馈在线用户信息,用户向服务器发送信息,服务器处理用户的数据,然后用户开始聊天。
客户端的聊天分为对所有人的信息和私聊的信息,该信息应通过程序控制分别进行处理。
图3.1设计流程图
4调试分析过程描述
⏹在聊天客服端启动的时候对端口进行监听,会出现图4.1的界面。
图4.1登陆服务器界面
⏹在客户端启动的时候,会出现图4.2的界面,该界面为客户端的连接界面。
图4.2登陆客户端界面(客服端1)
图4.2登陆客户端界面(客服端2)
⏹图4.3为用户张三和李四连接服务器时的界面
图4.3张三登陆服务器
图4.3李四登陆服务器
⏹图4.4为用户李四发送消息的服务器界面
图4.4李四发送消息服务端界面
⏹图4.5为用户张三发送消息的服务器界面
图4.4张三发送消息服务端界面
⏹图4.6为用户李四发送消息的客服端界面
图4.6李四发送消息的客服端界面
⏹图4.7为用户张三发送消息的客服端界面
图4.7张三发送消息的客服端界面
5核心代码
5.1服务端
(1).启动监听
UpdateData(TRUE);
//更新数据到类成员
GetDlgItem(IDC_BTNSTART)->
EnableWindow(FALSE);
GetDlgItem(IDC_BTNSEND)->
EnableWindow(TRUE);
g_ServerSocket=socket(AF_INET,SOCK_STREAM,0);
//创建套接字
if(INVALID_SOCKET==g_ServerSocket)
{
MessageBox("
创建套接字失败!
"
);
return;
}
SOCKADDR_INsvraddrsock;
svraddrsock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
svraddrsock.sin_family=AF_INET;
svraddrsock.sin_port=htons(m_port);
if(SOCKET_ERROR==bind(g_ServerSocket,(SOCKADDR*)&
svraddrsock,sizeof(SOCKADDR)))
套接字绑定失败!
listen(g_ServerSocket,10);
//监听套接字
//创建接收线程
m_hAcceptthread=CreateThread(NULL,0,AcceptThread,NULL,0,NULL);
if(m_hAcceptthread==NULL)
创建接收连接线程失败!
g_hmutex=CreateMutex(NULL,FALSE,NULL);
//创建互斥量
(2).监听请求连接线程
//接收连接
DWORDWINAPIAcceptThread(LPVOIDlpParameter)
{
SOCKADDR_INserveraddr;
intaddrlen=sizeof(SOCKADDR);
SOCKETRecvSocket;
//recv后返回的套接字
RecvSocket=accept(g_ServerSocket,(SOCKADDR*)&
serveraddr,&
addrlen);
g_ClientSocket=RecvSocket;
if(INVALID_SOCKET==RecvSocket)
AfxMessageBox("
接受连接失败!
returnFALSE;
}
BOOLContinueFlag=TRUE;
while(ContinueFlag&
&
TRUE)
g_Mutex.Lock();
BYTErecvBuff[RECV_DATA_SIZE+2]={0};
intrecvlength=sizeof(recvBuff);
intrecvedSize=0;
recvedSize=recv(RecvSocket,(char*)recvBuff,recvlength,0);
if(SOCKET_ERROR==recvedSize)
{
intx=WSAGetLastError();
CStringstr;
//WSANOTINITIALISED
str.Format("
错误代码:
%d"
x);
AfxMessageBox(str);
break;
}
CStringArrayRecvData;
UINTCmdFlag;
CChatRoomServerDlg:
:
Split((char*)recvBuff,'
|'
RecvData);
//将收到的数据进行分离
if(RecvData.GetSize()<
=0)
continue;
CmdFlag=atoi(RecvData.GetAt(0).GetBuffer(RecvData.GetAt(0).GetLength()));
inti=0;
//记录循环次数的变量
BOOLcomeFlag=TRUE;
//是否加入用户列表的标记
CTimeiotime;
CStringStrRecord="
;
//聊天内容
CStringstrTemp="
CStringstrTemp1="
charsendBuff[1024]={0};
intj=0;
CChatRoomServerDlg*ServerDlg=(CChatRoomServerDlg*)AfxGetApp()->
GetMainWnd();
//解析客服端发来的消息以便确定消息的类型
switch(CmdFlag)
caseCONNECT:
//用户发来的连接请求
//对用户发来的用户名和密码进行判断
strTemp=CString(RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength()));
ZeroMemory(sendBuff,1024);
//将JOIN命令和用户名填入缓冲区sprintf(sendBuff,"
5|%s|"
RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength()));
for(i=0;
i<
count;
i++)//向除自己以外的所有在线用户发送一条加入消息
{
//SOCKETtempsocket=((UserInfo*)UserList.GetAt(j))->
UserSocket;
if(RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength())!
=User[i].UserName)
{
if(SOCKET_ERROR==send(User[i].UserSocket,sendBuff,strlen(sendBuff),0))
{
AfxMessageBox("
connect中向各用户发送消息失败!
break;
}
}
}
//向该用户发送一条LIST消息使其将所有在线用户加入用户列表
strTemp="
3|"
for(i=0;
i++)
=
User[i].UserName)
strTemp+=User[i].UserName;
strTemp+="
|"
sprintf(sendBuff,"
%s"
strTemp);
if(count>
0)
if(SOCKET_ERROR==send(RecvSocket,sendBuff,strlen(sendBuff),0))
AfxMessageBox("
connect中发送List数据失败!
break;
//将该用户加入用户列表
if(RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength())==User[i].UserName)
comeFlag=FALSE;
if(comeFlag)
{
User[count].UserName=
RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength());
User[count].UserSocket=RecvSocket;
count++;
//将用户登陆成功发送给用户
sprintf(sendBuff,"
1|%s|"
RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength()));
if(SOCKET_ERROR==send(RecvSocket,sendBuff,strlen(sendBuff),0))
AfxMessageBox("
CONNECT中向客户端发送登陆成功消息失败!
break;
//在服务器上加入用户信息ServerDlg->
AddToUserList(
//将用户加入用户列表
strTemp=RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength());
iotime=CTime:
GetCurrentTime();
strTemp1=iotime.Format("
于:
%Y年%m月%d日%H:
%M:
%S登录"
strTemp+=strTemp1;
strTemp1="
服务器"
ServerDlg->
SetChatRecord(strTemp1,strTemp);
break;
caseCHAT:
//群聊
ZeroMemory(sendBuff,1024);
//向所有其他在线的用户转发收到的消息sprintf(sendBuff,"
2|%s|%s|"
RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength()),
RecvData.GetAt
(2).GetBuffer(RecvData.GetAt
(2).GetLength()));
for(i=0;
send(User[i].UserSocket,sendBuff,strlen(sendBuff),0);
//将收到的消息显示在服务器消息记录框中ServerDlg->
SetChatRecord(
RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength()),RecvData.GetAt
(2).GetBuffer(RecvData.GetAt
(2).GetLength()));
break;
caseEXIT:
i++)//删除用户在UserList中的信息
{if(RecvData.GetAt
(1).GetBuffer(RecvData.GetAt
(1).GetLength())==
//UserList.RemoveAt(i);
for(j=i;
j<
j++)
User[j].UserName=User[j+1].UserName;
User[j].UserSocket=User[j+1].UserSocket;
}
}
//把退出的用户从服务器用户列表中删除,同时向聊天记录中更新一条退出的消息
DeleteFromUserList(strTemp);
//将用户从服务器“用户列表”中删除
%S下线"
//向其他用户发送QUit消息
ZeroMemory(sendBuff,1024)
sprintf(sendBuff,"
6|%s|"