网络聊天室
1 需求分析
1.1 系统概述
在互联网迅速发展的今天,由于网络聊天室的即时通讯特点能够充分体现网络互联的优势,因此已经成为互联网上不可缺少的一部分。本系统实现了网络聊天室的基本功能,系统由服务器和客户端两部分组成,用户通过客户端登录到服务器后,可以与其他登录到服务器的用户聊天,也就是说,用户可以在聊天室内发表言论,并且彼此之间能够看到对方的谈话。
1.2 系统运行环境
1.硬件环境
· 处理器: Inter Pentium 166 MX 或更高
· 内存: 32M以上
· 硬盘空间:1GB
· 显卡: SVGA显示适配器
2.软件环境
· 操作系统: Windows 98/ME/2000/XP
1.3 功能需求描述
网络聊天室的总体目标是为网络用户提供一个方便易用的即时通讯工具。
需要实现以下一些基本功能:
1. 消息发送:用户可以发送消息给聊天室里所有的或者指定的其他用户。
2. 消息接收:用户可以接收到其他用户发送给他的消息。
3. 聊天室管理功能:聊天室管理员可以启动或者关闭聊天室服务,并且对于违反聊天室规则的用户可以强制其离开聊天室。
2 总体设计
2.1 开发与设计的总体思想
网络聊天室作为网络多用户软件,其界面操作的方便性、稳定性和服务器的性能是决定软件质量的首要条件,所以这几方面应该作为开发与设计的重点。
系统采用结构化的设计方法进行设计,采用快速开发平台Delphi 6进行开发。
2.2 系统模块结构图
依据需求分析结果,网络聊天室主要分成两个模块。如图2-1所示。
图2-1 系统模块结构图
2.3 模块设计
1.服务器模块
服务器模块主要完成接收并处理用户登录
,转发登录用户发送的消息、启动聊天服务、关闭聊天服务和踢开指定用户功能。该模块应包括三个子功能模块:处理用户登录申请功能、转发用户消息功能、聊天室管理功能。模块功能树如图2-2所示。
图2-2 服务器模块功能树图
2.客户端模块
客户端模块为用户提供聊天室操作界面,用户通过客户端可以发送消息给全部或指定的其他用户,可以看到聊天内容,可以看到聊天室内有哪些用户在线。客户端模块功能树如图2-3所示。
图2-3 客户端模块功能树图
2.4 系统流程描述
系统主体的处理流程如图2-4所示。
图2-4 系统主体处理流程图
系统客户端和服务器的主体处理流程如图2-5和图2-6所示。
图2-5 客户端处理流程图
图2-6 服务器处理流程图
2.5 界面设计
界面设计应遵循简洁美观、方便易用的基本原则。
1.服务器界面设计
具体服务器界面设计效果图如图2-7所示。
图2-7 服务器界面设计效果图
2.客户端界面设计
登录界面设计效果图如图2-8所示,客户端主体界面设计效果图如图2-9所示。
图2-8 客户端登录界面设计效果图
图2-9 客户端界面设计效果图
3 详细设计
基于系统需求分析与系统总体设计的结论,本系统采用Delphi 7实现各模块的功能,下面按照模块的划分来分别阐述系统的详细设计和实现过程。
3.1 服务器模块
1.功能描述
接收并处理客户端的登录请求,并根据客户端的聊天请求向指定客户端转发聊天内容,同时提供聊天室用户管理和服务管理功能。
2.IPO图
输入:客户端请求消息
处理:
① 启动服务器服务;
② 等待接收客户端请求;
③ 根据客户端请求类型,做不同处理;
④ 将处理结果发送给客户端。
输出:发给客户端的应答消息
3.函数/过程设计
过程1: startClick ()
声明:procedure TdmMain.startClick(Sender: TObject);
功能:启动服务器的聊天服务。
IPO图:
输入:无
处理:
① 判断服务器服务进程状态,如果服务已经启动则过程结束,否则进入下一步;
② 设置服务器服务进程的Socket端口号为2345;
③ 启动服务器服务进程;
④ 在界面显示服务启动提示信息。
输出:服务器启动信息
程序清单:
procedure TdmMain.startClick(Sender: TObject);
begin
if ServerSocket_Chat.Active=false then
begin
//设置服务器服务进程的Socket端口号
ServerSocket_Chat.Port:=2345;
//启动服务器服务进程
ServerSocket_Chat.Active :=true;
//在界面显示服务启动提示信息
ServerStatus.Lines.add(datetostr(date)+' '
+timetostr(time)+':'+'服务启动');
end
end;
过程2:stopClick ()
声明:procedure TdmMain.stopClick(Sender: TObject);
功能:关闭聊天室聊天服务。
IPO图:
输入:无。
处理:
① 判断服务器服务进程状态,如果服务已经关闭则过程结束,否则进入下一步;
② 关闭服务器服务进程;
③ 在界面显示服务关闭提示信息。
输出:聊天室关闭信息
程序清单:
procedure TdmMain.stopClick(Sender: TObject);
begin
if ServerSocket_Chat.Active=true then
begin
//关闭服务器服务进程
ServerSocket_Chat.Active :=false;
//在界面显示服务关闭提示信息
ServerStatus.Lines.add(datetostr(date)+' '
+timetostr(time)+':'+'关闭');
end
end;
过程3: ServerSocket_ChatClientRead ()
声明:procedure TdmMain.ServerSocket_ChatClientRead(Sender: TObject;
Socket: TCustomWinSocket);
功能:接收并处理客户端请求。
IPO图:
输入:无
处理:
① 获取客户端发送的请求消息;
② 解析请求消息;
③ 根据请求消息类型做不同的应答处理。
输出:各项游戏初始化数据。
程序清单:
procedure TdmMain.ServerSocket_ChatClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var strReceive,strCom,strContent,strNick,strFriend:string;
n,i:integer;
newItem:TListItem;
begin
//获取客户端发送的请求消息
strReceive:= Socket.ReceiveText;
//解析请求消息
n:=pos('~',strReceive);
if n>0 then
begin
//获取请求指令类型
strCom:=copy(strReceive,1,n-1);
//处理客户端的广播聊天请求
if strCom='public' then //public~content
begin
//获取聊天内容
strContent:=copy(strReceive,n+1,length(strReceive)-n);
//构造消息应答,并将应答消息发送给所有在线客户端
for i:=0 to ServerSocket_Chat.Socket.ActiveConnections-1 do
begin
ServerSocket_Chat.Socket.Connections[i]
.SendText('talk~'+strContent);
end;
end;
//处理客户端的私聊请求
if strCom='private' then //private~nick~nick~content
begin
//获取请求内容
strContent:=copy(strReceive,n+1,length(strReceive)-n);
//获取消息发送者昵称
strNick:= copy(strContent,1,pos('~',strContent)-1);
strContent:=copy(strContent,pos('~',strContent)+1
,length(strContent)-pos('~',strContent));
//获取消息接收者昵称
strFriend:= copy(strContent,1,pos('~',strContent)-1);
//获取消息内容
strContent:=copy(strContent,pos('~',strContent)+1
,length(strContent)-pos('~',strContent));
//构造消息应答,并将应答消息发送给消息接收者
for i:=0 to ServerSocket_Chat.Socket.ActiveConnections-1 do
begin
if UserList.Items.Item[i].Caption = strFriend then
ServerSocket_Chat.Socket.Connections[i]
.SendText('talk~'+strContent);
end;
end;
//处理客户端的登录服务器请求
if strCom='request' then //request~nick
begin
//获取登录用户昵称
strNick:=copy(strReceive,n+1,length(strReceive)-n);
//检查是否存在重名用户,如果有则返回错误应答消息
for i:= 0 to UserList.Items.Count -1 do
begin
if UserList.Items.Item[i].Caption = strNick then
begin
ServerSocket_Chat.Socket.Connections[UserList.Items.Count]
.SendText('err~'+strNick);
exit;
end;
end;
//将登录用户添加到在线用户列表
newitem:=UserList.Items.insert(UserList.Items.count);
newitem.caption:=strNick;
newitem.SubItems.Add(socket.RemoteAddress);
newitem.SubItems.Add(socket.RemoteHost);
AllUserList:=AllUserList+'~'+strNick;
//向所有在线用户广播用户登录消息
for i:=0 to ServerSocket_Chat.Socket.ActiveConnections-1 do
begin
ServerSocket_Chat.Socket.Connections[i]
.SendText('add'+AllUserList+'~');
end;
end;
end;
end;
过程4:ServerSocket_ChatClientDisconnect ();
声明:procedure TdmMain.ServerSocket_ChatClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
功能:处理用户离开情况。
IPO图:
输入:无
处理:
① 在用户列表中删除离开用户;
② 重新构造在线用户名单;
③ 向所有在线用户广播用户离开指令和在线用户名单。
输出:无
程序清单:
procedure TdmMain.ServerSocket_ChatClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var i:integer;
strNick:string;
isDel:boolean;
begin
//清空在线用户名单
AllUserList:='';
isDel:=false;
//在用户列表中删除离开用户
for i:=0 to UserList.Items.count -1 do
begin
if not isDel then
if socket.RemoteAddress=UserList.Items[i]
.subitems[0] then
begin
strNick:= UserList.Items[i].caption;
UserList.Items.Item[i].Delete();
isDel:=true;
end
end;
//重新构造在线用户名单
for i:=0 to UserList.Items.count -1 do
begin
AllUserList:=AllUserList+'~'+ UserList.Items[i].caption;
end;
//向所有在线用户广播用户离开指令和在线用户名单
for i:=0 to ServerSocket_Chat.Socket.ActiveConnections-1 do
ServerSocket_Chat.Socket.Connections[i]
.SendText('del~'+strNick+AllUserList+'~');
end;
过程5:KickClick ()
声明:procedure TdmMain.KickClick(Sender: TObject);
功能:将管理员指定用户从聊天室中踢开。
IPO图:
输入:无
处理:
① 判断当前是否选中需要踢开的用户;
② 如果有选中的用户,将选中用户的Socket连接关闭;
输出:无
程序清单:
procedure TdmMain.KickClick(Sender: TObject);
begin
//判断当前是否选中需要踢开的用户
if UserList.ItemIndex>=0 then
//将选中用户的Socket连接关闭
ServerSocket_Chat.Socket.Connections[UserList.ItemIndex].Close;
end;
3.2 客户端模块
1.功能描述
为聊天用户提供聊天操作界面,主要是向服务器发送聊天请求,并根据服务器的应答进行不同的处理。
2.IPO图
输入:用户操作
处理:
① 登录聊天室服务器;
② 根据聊天要求构造请求消息,并向服务器发送;
③ 解析服务器的应答消息,并进行相应处理。
输出:提示消息和聊天内容
3.函数/过程设计
过程1: bnt_loginClick ()
声明:procedure TMainForm.bnt_loginClick(Sender: TObject);
功能:登录聊天室服务器。
IPO图:
输入:无
处理:
① 启动登录窗口,等待用户输入服务器IP地址和用户昵称;
② 初始化Socket通讯配置;
③ 建立Socket连接。
输出:无
程序清单:
procedure TMainForm.bnt_loginClick(Sender: TObject);
begin
//创建并显示登录窗口
if login = nil then
login := TLogin.Create(nil);
login.ShowModal;
//当用户选择登录后,根据用户设置的服务器IP地址和昵称登录服务器
if login.isOk then
begin
//设置聊天室服务器服务的Socket端口号
clientsocket1.Port:=2345;
//设置聊天室服务器服务的IP地址
clientsocket1.host:=login.Edit1.Text;
//建立Socket连接
clientsocket1.active:=true;
MyName:= login.Edit2.Text
end;
end;
过程2: ClientSocket1Connect ()
声明:procedure TMainForm.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
功能:向聊天室服务器发送登录请求。
IPO图:
输入:无
处理:
① 向聊天室服务器发送登录请求;
② 等待服务器应答。
输出:登录请求消息
程序清单:
procedure TMainForm.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
//向聊天室服务器发送登录请求
clientsocket1.Socket.SendText('request~'+MyName);
end;
过程3: ClientSocket1Read ()
声明:procedure TMainForm.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
功能:接收服务器应答消息,并进行处理。
IPO图:
输入:应答消息
处理:
① 获取服务器应答消息;
② 解析应答消息;
③ 根据应答消息类型进行处理。
输出:无
程序清单:
procedure TMainForm.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var strReceive,strCom,strContent,strUser:string;
n,i:integer;
begin
//获取服务器应答消息
strReceive:=socket.ReceiveText;
//解析应答消息
strCom:=copy(strReceive,1,pos('~',strReceive)-1);
strContent:='';
strUser:='';
//根据应答消息类型进行处理
//应答消息为新用户进入聊天室
if strCom='add' then
begin
//重新构造用户列表
UserList.Clear;
UserList.Items.Add('大家');
UserList.ItemIndex:=0;
strContent:=copy(strReceive,pos('~',strReceive)+1
,length(strReceive)-pos('~',strReceive));
repeat
strUser:=copy(strContent,1,pos('~',strContent)-1);
UserList.Items.Add(strUser);
strContent:=copy(strContent,pos('~',strContent)+1
,length(strContent)-pos('~',strContent));
until strContent='';
//更新聊天室聊天内容
Chat_Content.Lines.Add(UserList.Items
.Strings[UserList.Items.count-1]+'进来了,大家欢迎!');
end;
//应答消息为用户离开聊天室
if strCom='del' then
begin
//更新聊天室用户列表
UserList.Clear;
UserList.Items.Add('大家');
UserList.ItemIndex:=0;
strContent:=copy(strReceive,pos('~',strReceive)+1
,length(strReceive)-pos('~',strReceive));
strUser:=copy(strContent,1,pos('~',strContent)-1);
strContent:=copy(strContent,pos('~',strContent)+1
,length(strContent)-pos('~',strContent));
//更新聊天室内容
Chat_Content.Lines.Add(strUser+'离开了...');
repeat
strUser:=copy(strContent,1,pos('~',strContent)-1);
UserList.Items.Add(strUser);
strContent:=copy(strContent,pos('~',strContent)+1
,length(strContent)-pos('~',strContent));
until strContent='';
end;
//应答消息为聊天内容
if strCom='talk' then
begin
//获取聊天内容
strContent:=copy(strReceive,pos('~',strReceive)+1
,length(strReceive)-pos('~',strReceive));
//更新聊天室内容显示
Chat_Content.Lines.Add(strContent);
end;
//应答消息为登录错误
if strCom='err' then
begin
//获取消息内容
strContent:=copy(strReceive,pos('~',strReceive)+1
,length(strReceive)-pos('~',strReceive));
//提示用户错误
showmessage('<'+strContent+'>'+'这个昵称已经存在,请换一个登录!');
repeat
strUser:=inputbox('重新输入','请输入昵称','大力水手');
until trim(strUser)<>'';
//重新发送登录请求
clientsocket1.Socket.SendText('request~'+strUser);
end;
end;
过程4: sendClick ()
声明:procedure TMainForm.sendClick(Sender: TObject);
功能:向聊天室服务器发送聊天内容转发请求。
IPO图:
输入:聊天内容
处理:
① 检查发送消息内容是否为空;
② 根据用户选择的聊天对象,向聊天室服务器发送广播请求;
③ 如果用户选择私聊,向服务器发送私聊请求。
输出:聊天内容转发请求
程序清单:
procedure TMainForm.sendClick(Sender: TObject);
var strFriend:string;
begin
//检查发送消息内容是否为空
if trim(Edit_talk.Text)='' then
exit;
//根据用户选择的聊天对象,向聊天室服务器发送广播请求
if UserList.ItemIndex=0 then
begin
clientsocket1.Socket.SendText('public~'+MyName+'对大家说:'+Edit_talk.Text);
Edit_talk.Text:='';
exit;
end ;
strFriend:=UserList.Items.Strings[UserList.ItemIndex];
if CheckBox1.Checked = false then
begin
if MyName= strFriend then
clientsocket1.Socket.SendText('public~'+MyName+'自言自语道:'+Edit_talk.Text)
else
clientsocket1.Socket.SendText('public~'+MyName+'对'+strFriend+'说:'+Edit_talk.Text)
end
else
//如果用户选择私聊,向服务器发送私聊请求
begin
clientsocket1.Socket.SendText('private~'+MyName+'~'+strFriend+'~'+MyName+'对你悄悄地说:'+Edit_talk.Text)
end;
Edit_talk.Text:='';
end;
过程5: outClick ()
声明:procedure TMainForm.outClick(Sender: TObject);
功能:离开聊天室。
IPO图:
输入:无
处理:关闭与聊天室服务器的Socket连接。
输出:无
程序清单:
procedure TMainForm.outClick(Sender: TObject);
begin
clientsocket1.active:=false;
end;