即时聊天系统.docx
《即时聊天系统.docx》由会员分享,可在线阅读,更多相关《即时聊天系统.docx(30页珍藏版)》请在冰豆网上搜索。
即时聊天系统
《网络应用程序设计》课程结业设计
课题名称:
并发服务器编程
组长:
丁成园
组员:
曹昀鑫,周峰江
学院:
计算机科学与工程学院
专业:
08网络工程
(1)班
学号:
090208124
指导教师:
李克清
提交日期:
2011/6/23
目录
1.引言......................................................................3
2.分析过程..................................................................3
2.1基本原理.............................................................3
2.2总体框架.............................................................3
3.开发过程4
3.1主要模块的详细设计...................................................5
3.11................................................................5
3.12................................................................5
3.13................................................................5
3.2运行.................................................................5
4.测试过程..................................................................9
5.实验小结..................................................................9
附录
1主要代码...................................................................11
1.1服务器主要代码.......................................................11
1.2客户端主要代码.......................................................17
2使用说明书.................................................................22
基于C#的聊天系统
1.引言
随着现在技术的不断输入和信息化得广泛普及,网络作为一种新兴事物,在近些年得到了前所未有的普及和深入,网络上的各种应用层出不穷,在各个阶层、各个年龄段得用户提供了不同的个性化的服务。
而现代中小型企业的领导认识到进一步提高企业信息化的必要性。
为使企业能在瞬间万变的信息时代生存下去,适应激烈的市场竞争,现在企业要求正加员工之间的交流和通信能力,尤其是领导和员工之间的共同能力,实时的交流系统就是在这种条件下诞生的。
系统开发时一个十分复杂的系统工程。
这里对系统开发的基本过程和方法进行介绍,列出相关的文档示例加以说明。
网络通信时网络应用的重要体现方面。
网上即时通信则是网络应用的重要体现方面。
本聊天系统主要应用于小型企业,学校或小型局域网内部使用。
本应用程序使短距离交流更加快捷和方便。
更能高效率的体现现代即时通信的特点。
2.分析过程
2.1基本原理
2.1.1服务器端
服务器启动后,将一直监听制定的端口。
看是否有客户端发来请求。
客户端如果要与服务器端建立连接,当客户端进行登录时,要向服务器端发送请求,将客户端的本地IP和端口号发送给服务器当服务器端接受到客户端发送来的请求后,会将客户端的资料储存在一个新的集合里,来满足其功能要求。
2.12客户端
当客户端建立于服务器端的连接后,就可以实现聊天的功能了。
任何一个客户端需要跟另一个客户联系时,客户端会将发送的内容、目标地址的IP地址和端口号一起发送给服务器,然后服务器会在socket对象中扫描出该目的地址并将客户端发送的内容再转发给目的地址。
2.13其他细节
在各个组件和容器中需要有事件处理的地方,比如关闭对话框,确定和取消等,要事先设置事件监听,和事件处理。
由于允许使用多个客户端,所以在各个套接字在接发信息时要用多线程,使接发信息的类继承Tread,实现多线程设计。
为了不使客户端和服务器端的程序代码过长,把各个类封装在不同的文件中,并都声明为公共类(public),这样可以在同一个包内访问。
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
通过委托,实现了客户端能够不停地接收服务器发送来的信息,使得信息能够及时显示。
2.2总体框架
程序流程图
程序分析图
3.开发过程
3.1主要模块的详细设计
3.11登录
客户端首先登录,用户输入登录信息,点击“登录”按钮,如果服务器没有开启,显示“无法连接”的提示信息,如服务开启了,客户端根据接收的信息,调用数据库,查看数据库中是否存在此信息,如果不存在,显示出错信息;信息存在,向服务器发送登录信息,服务器接收信息后判断发现是登录信息,于是向数据库写入信息,此时写入的信息中的离线时间为空,客户端接收信息,同时调用数据库获得在线好友信息并回应给对应的客户端。
客户端接收服务器发来的信息,获得该用户的在线好友信息,进入聊天页面,同时将在线好友信息显示到好友列表中显示。
3.12聊天通信
客户端选择好友,在信息框中输入聊天信息,点击“发送”按钮时,首先在聊天框显示聊天内容,同时将信息送往服务器,服务器收到信息后,判断信息类型,是聊天信息,从信息中提取目的客户端的账号信息,根据账号信息,调用数据库,查看与其对应的IP地址和端口号,然后将信息重新打包发送往目的客户端,目的客户端收到服务器发来信息,根据源账号信息,得出对应的名称以便显示在聊天信息框中,同时将发送来的信息也一并显示在聊天信息框中。
3.13退出
客户端退出时,向服务器发送消息,服务器收到信息后,判断信息类型,得到是退出消息,获取当前时间,根据信息中的账号跟新数据库,将与此账号对应的且离线时间为空的信息条进行更新。
3.2运行
首先打开服务器客户端,点击打开服务器按钮,如图1所示。
图1服务器客户端
打开用户登录界面,并输入账号,密码,进入系统。
如图2所示。
用户登录界面
当用户111登录后,会显示如图3所示的聊天框,显示聊天信息和好友列表。
好友222登录后,用户111的登录界面会出现如图4所示。
图3用户111登录后聊天界面
图4用户222登录后聊天界面
这个时候,服务器会刷新所有在线用户列表如图5所示。
图5刷新用户列表后的服务器客户端
当用户退出后,会出现与服务器断开连接的提示框如图6所示。
图6用户退出界面
当用户111向用户222发送聊天信息时,用户111的聊天界面会显示如图7所示聊天信息,用户222接收到用户111发出的消息后,在聊天界面会出现如图8所示聊天信息。
图7用户111的聊天界面
图8用户222的聊天界面
4.测试过程
经过对系统的软、硬件的验收测试系统满足课题的要求。
其中硬件的测试主要包括硬件设备的清单、启动运行正常与否等。
重点是软件部分的测试,首先对软件的功能进行测试,然后再测试其稳定性。
系统功能测试细则表
序号
测试项目
功能与指标
测试方法
预期方法
测试结果
1
可登陆性
用户可以随时登陆,进入聊天室
按界面提示输入名称
出现主窗口和各个菜单项
正确完成
2
发出消息给好友
可以出送消息给好友
在消息框输入消息,并选择好友
好友收到消息
正确完成
3
接收到消息
用户能接收到其他好友发出的消息
当接收到数据时,在窗口显示
显示接收到的消息
正确完成
4
登陆后好友上线提示
在登陆状态下,能够接收到上线通知
启动多个客户端,连接到服务器端
其他好友显示上线
正确完成
5.实验小结
通过这次试验,我更加了解了关于C#编程过程中容易遇到的问题,以及系统的开发过程和检测,实践性的学习并使用了软件工程中学习到的知识,软件的制作全过程。
通过这次试验,让我受益匪浅。
附录
1.主要代码
1.1服务器主要代码
(1)变量定义:
privateTcpListenerServerListener;
//privateIPEndPointServerInfo;//存放服务器的IP和端口信息
privateSocketServerSocket;//服务端运行的SOCKETServerSocket
privateThreadListenThread;//服务端运行的线程
privateThreadAcceptThread;
privateArrayListClientsList;//为客户端建立的SOCKET连接
(2)“开启服务器”Click事件代码:
privatevoidbtnOpen_Click(objectsender,EventArgse)
{
ClientsList=newArrayList();
//新开一个监听线程
ListenThread=newThread(newThreadStart(ListenClients));
ListenThread.Start();
this.btnOpen.Enabled=false;
this.btnClose.Enabled=true;
}
(3)监听客户端:
privatevoidListenClients()
{
//监听
ServerListener=newTcpListener(IPAddress.Parse("10.18.46.7"),8000);
ServerListener.Start(10);
//新开一个socket去接收客户端
while(true)
{
try
{
SocketsktAccept=ServerListener.AcceptSocket();
ServerSocket=sktAccept;
AcceptThread=newThread(newThreadStart(MassageHandle));
AcceptThread.Start();
}
catch(Exception)
{
}
}
}
(4)信息接收处理:
privatevoidMassageHandle()
{
SocketsktAccept=ServerSocket;
ThreadtdAccept=AcceptThread;
string[]message=newstring[2];
byte[]bytes=newbyte[1024];
stringuid="";
IPEndPointie=(IPEndPoint)sktAccept.RemoteEndPoint;
boolbRcvMsg=true;
try
{
while(bRcvMsg)
{
Byte[]buffer=newByte[1024];
//接收子画面传过来的内容
intrec=sktAccept.Receive(buffer);
stringmsg=Encoding.ASCII.GetString(buffer,0,rec);
message=TransMessage(msg);
bytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
uid=message[0].Trim().ToString();
if(message[1]!
=null)
{
stringname=GetSqlconStr.getName(uid);
if(message[1].Trim()=="ALL")
{//全发送
for(inti=0;i{
SendMsgToClients((CList)ClientsList[i],msg);
}
//发送给单个客户端
}
else
{
stringmyid=message[1].Trim();
IPEndPointiep=GetSqlconStr.getEndPoint(myid);
for(inti=0;i{
if(((IPEndPoint)((CList)ClientsList[i]).Sockt.RemoteEndPoint).Equals(iep))
{
SendMsgToClients((CList)ClientsList[i],msg);
break;
}
}
}
}
elseif(message[2].ToString()=="LoginIn")
{
//Invoke改变主机登录名单(增加登录机到主机名单List)
ChangeLb(uid,true);
for(inti=0;i{
SendMsgToClients((CList)ClientsList[i],msg);
}
//新建一个CList类对象,加入到服务器的客户端列表
CListclient=newCList(message[0],sktAccept.RemoteEndPoint,tdAccept,sktAccept);
ClientsList.Add(client);
//给新登录的客户端发送已登录人的名单
stringuserList="List|";
for(inti=0;i{
userList+=lbUsers.Items[i].ToString()+"|";
}
//buffer=Encoding.ASCII.GetBytes(userList.ToCharArray());
//给新登录的客户端发送登录人名单
SendMsgToClients(client,userList);
SqlConnectioncon1=newSqlConnection(GetSqlconStr.getConstring());
con1.Open();
stringsql1="insertintoLoginStatus(UserId,IpAddress,Port,LoginInTime,LoginOutTime)values('"+uid+"','"+ie.Address.ToString()+"','"+ie.Port.ToString()+"','"+DateTime.Now.ToString()+"',null)";
SqlCommandcmd1=newSqlCommand(sql1,con1);
cmd1.ExecuteNonQuery();
con1.Close();
}
//退出
elseif(message[2].ToString()=="LoginOut")
{
SqlConnectioncon2=newSqlConnection(GetSqlconStr.getConstring());
con2.Open();
stringsql2="updateLoginStatussetLoginOutTime='"+DateTime.Now.ToString()+"'whereUserId='"+uid+"'andLoginOutTimeisNULL";
SqlCommandcmd2=newSqlCommand(sql2,con2);
cmd2.ExecuteNonQuery();
con2.Close();
SqlConnectioncon3=newSqlConnection(GetSqlconStr.getConstring());
con3.Open();
stringsql3="updateUserssetStatus='离线'whereUserId='"+uid+"'";
SqlCommandcmd3=newSqlCommand(sql3,con3);
cmd3.ExecuteNonQuery();
con3.Close();
ChangeLb(uid,false);
intnRemove=0;
for(inti=0;i{
if(((CList)ClientsList[i]).Sockt.RemoteEndPoint.ToString().Equals(sktAccept.RemoteEndPoint.ToString()))
{
nRemove=i;
bRcvMsg=false;
break;
}
}
sktAccept.Close();
ClientsList.Remove(ClientsList[nRemove]);
stringstrSendMsg=nRemove.ToString()+"||"+"LoginOut";
for(inti=0;i{
buffer=Encoding.ASCII.GetBytes(strSendMsg);
//给新登录的客户端发送登录人名单
SendMsgToClients((CList)ClientsList[i],strSendMsg);
}
}
}
}
catch(Exception)
{
ListenThread.Abort();
ServerSocket.Close();
AcceptThread.Abort();
}
}
(5)服务器向客户端发送在线好友名单:
privatevoidSendMsgToClients(CListclient,stringMsg)
{
try
{
byte[]bufferToClient=Encoding.ASCII.GetBytes(Msg.ToCharArray());
client.Sockt.Send(bufferToClient);
}
catch(Exception)
{
}
finally
{
}
}
delegatevoidchangeLb(stringstr,boolbIsAdd);
//访问主线程
privatevoidChangeLb(stringstr,boolbIsAdd)
{
//invoke调用主线程
this.Invoke(newchangeLb(ChangeLbText),str,bIsAdd);
}
//设置主线程[界面]的值
privatevoidChangeLbText(stringstr,boolbIsAdd)
{
if(bIsAdd)
{
lbUsers.Items.Add(str);
}
else
{
lbUsers.Items.Remove(str);
}
}
(6)将收到的信息根据“|”分开,以便与判断信息类型:
privatestring[]TransMessage(stringtmsg)
{
intsum=0;
string[]reMessage=newstring[3];
char[]list=tmsg.ToCharArray();
for(inti=0;i{
if(list[i]=='|'&&sum<2)
{
sum=sum+1;
}
else
{
reMessage[sum]+=Convert.ToString(list[i]);
}
}
returnreMessage;
}
(7)运行服务器时的初始设置:
privatevoidServer_Load(objectsender,EventArgse)
{
cmboxStatus.SelectedIndex=0;
this.btnOpen.Enabled=true;
this.btnClose.Enabled=false;
}
(8)信息刷新,以便于及时显示上线用户:
privatevoidRefresh(stringsql,DataGridViewdgw)
{
SqlConnectionconn=newSqlConnection(GetSqlconStr.getConstring());
conn.Open();
SqlCommandcmd=newSqlCommand(sql,conn);
SqlDataAdapterda=newSqlDataAdapter(cmd);
DataSetds=newDataSet("Users");
da.Fill(ds);
dgw.DataSource=ds.Tables[0];
conn.Close();
}
(9)根据条件选择显示对应的用户信息:
privatevoidcmboxStatus_SelectedIndexChanged(objectsender,EventArgse)
{
stringsql="";
if(cmboxStatus.SelectedItem.ToString()=="在线")
sql="select*fromUserswherestatus='在线'";
elseif(cmboxStatus.Sel