1、即时聊天系统网络应用程序设计课程结业设计课题名称:并发服务器编程组 长:丁成园组 员:曹昀鑫,周峰江学 院:计算机科学与工程学院专 业:08网络工程(1)班学 号:090208124指导教师:李克清提交日期:2011/6/23目录1. 引言.32. 分析过程.32.1基本原理.32.2总体框架.33. 开发过程43.1主要模块的详细设计.53.11.53.12.53.13.53.2运行.54. 测试过程.95. 实验小结.9附录1主要代码.111.1服务器主要代码.111.2客户端主要代码.172使用说明书.22基于C#的聊天系统1. 引言随着现在技术的不断输入和信息化得广泛普及,网络作为一种
2、新兴事物,在近些年得到了前所未有的普及和深入,网络上的各种应用层出不穷,在各个阶层、各个年龄段得用户提供了不同的个性化的服务。而现代中小型企业的领导认识到进一步提高企业信息化的必要性。为使企业能在瞬间万变的信息时代生存下去,适应激烈的市场竞争,现在企业要求正加员工之间的交流和通信能力,尤其是领导和员工之间的共同能力,实时的交流系统就是在这种条件下诞生的。系统开发时一个十分复杂的系统工程。这里对系统开发的基本过程和方法进行介绍,列出相关的文档示例加以说明。网络通信时网络应用的重要体现方面。网上即时通信则是网络应用的重要体现方面。本聊天系统主要应用于小型企业,学校或小型局域网内部使用。本应用程序使
3、短距离交流更加快捷和方便。更能高效率的体现现代即时通信的特点。2. 分析过程2.1基本原理2.1.1服务器端服务器启动后,将一直监听制定的端口。看是否有客户端发来请求。客户端如果要与服务器端建立连接,当客户端进行登录时,要向服务器端发送请求,将客户端的本地IP和端口号发送给服务器当服务器端接受到客户端发送来的请求后,会将客户端的资料储存在一个新的集合里,来满足其功能要求。2.12客户端 当客户端建立于服务器端的连接后,就可以实现聊天的功能了。任何一个客户端需要跟另一个客户联系时,客户端会将发送的内容、目标地址的IP地址和端口号一起发送给服务器,然后服务器会在socket对象中扫描出该目的地址并
4、将客户端发送的内容再转发给目的地址。2.13其他细节在各个组件和容器中需要有事件处理的地方,比如关闭对话框,确定和取消等,要事先设置事件监听,和事件处理。由于允许使用多个客户端,所以在各个套接字在接发信息时要用多线程,使接发信息的类继承Tread,实现多线程设计。为了不使客户端和服务器端的程序代码过长,把各个类封装在不同的文件中,并都声明为公共类(public),这样可以在同一个包内访问。委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。通过
5、委托,实现了客户端能够不停地接收服务器发送来的信息,使得信息能够及时显示。2.2总体框架程序流程图程序分析图3. 开发过程3.1主要模块的详细设计3.11登录客户端首先登录,用户输入登录信息,点击“登录”按钮,如果服务器没有开启,显示“无法连接”的提示信息,如服务开启了,客户端根据接收的信息,调用数据库,查看数据库中是否存在此信息,如果不存在,显示出错信息;信息存在,向服务器发送登录信息,服务器接收信息后判断发现是登录信息,于是向数据库写入信息,此时写入的信息中的离线时间为空,客户端接收信息,同时调用数据库获得在线好友信息并回应给对应的客户端。客户端接收服务器发来的信息,获得该用户的在线好友信
6、息,进入聊天页面,同时将在线好友信息显示到好友列表中显示。3.12聊天通信客户端选择好友,在信息框中输入聊天信息,点击“发送”按钮时,首先在聊天框显示聊天内容,同时将信息送往服务器,服务器收到信息后,判断信息类型,是聊天信息,从信息中提取目的客户端的账号信息,根据账号信息,调用数据库,查看与其对应的IP地址和端口号,然后将信息重新打包发送往目的客户端,目的客户端收到服务器发来信息,根据源账号信息,得出对应的名称以便显示在聊天信息框中,同时将发送来的信息也一并显示在聊天信息框中。3.13退出客户端退出时,向服务器发送消息,服务器收到信息后,判断信息类型,得到是退出消息,获取当前时间,根据信息中的
7、账号跟新数据库,将与此账号对应的且离线时间为空的信息条进行更新。3.2运行首先打开服务器客户端,点击打开服务器按钮,如图1所示。图1 服务器客户端打开用户登录界面,并输入账号,密码,进入系统。如图2所示。用户登录界面 当用户111登录后,会显示如图3所示的聊天框,显示聊天信息和好友列表。好友222登录后,用户111的登录界面会出现如图4所示。图3用户111登录后聊天界面图4用户222登录后聊天界面这个时候,服务器会刷新所有在线用户列表如图5所示。图5刷新用户列表后的服务器客户端当用户退出后,会出现与服务器断开连接的提示框如图6所示。图6用户退出界面当用户111向用户222发送聊天信息时,用户1
8、11的聊天界面会显示如图7所示聊天信息,用户222接收到用户111发出的消息后,在聊天界面会出现如图8所示聊天信息。图7用户111的聊天界面图8用户222的聊天界面4. 测试过程经过对系统的软、硬件的验收测试系统满足课题的要求。其中硬件的测试主要包括硬件设备的清单、启动运行正常与否等。重点是软件部分的测试,首先对软件的功能进行测试,然后再测试其稳定性。系统功能测试细则表序号测试项目功能与指标测试方法预期方法测试结果1可登陆性用户可以随时登陆,进入聊天室按界面提示输入名称出现主窗口和各个菜单项正确完成2发出消息给好友可以出送消息给好友在消息框输入消息,并选择好友好友收到消息正确完成3接收到消息用
9、户能接收到其他好友发出的消息当接收到数据时,在窗口显示显示接收到的消息正确完成4登陆后好友上线提示在登陆状态下,能够接收到上线通知启动多个客户端,连接到服务器端其他好友显示上线正确完成5. 实验小结通过这次试验,我更加了解了关于C#编程过程中容易遇到的问题,以及系统的开发过程和检测,实践性的学习并使用了软件工程中学习到的知识,软件的制作全过程。通过这次试验,让我受益匪浅。附录1. 主要代码1.1服务器主要代码 (1)变量定义: private TcpListener ServerListener; /private IPEndPoint ServerInfo;/存放服务器的IP和端口信息 pr
10、ivate Socket ServerSocket;/服务端运行的SOCKETServerSocket private Thread ListenThread;/服务端运行的线程 private Thread AcceptThread; private ArrayList ClientsList;/为客户端建立的SOCKET连接 (2)“开启服务器”Click事件代码: private void btnOpen_Click(object sender, EventArgs e) ClientsList = new ArrayList(); /新开一个监听线程 ListenThread = ne
11、w Thread(new ThreadStart(ListenClients); ListenThread.Start(); this.btnOpen.Enabled = false; this.btnClose.Enabled = true; (3)监听客户端: private void ListenClients() /监听 ServerListener = new TcpListener(IPAddress.Parse(10.18.46.7), 8000); ServerListener.Start(10); /新开一个socket去接收客户端 while (true) try Sock
12、et sktAccept = ServerListener.AcceptSocket(); ServerSocket = sktAccept; AcceptThread = new Thread(new ThreadStart(MassageHandle); AcceptThread.Start(); catch (Exception) (4)信息接收处理: private void MassageHandle() Socket sktAccept = ServerSocket; Thread tdAccept = AcceptThread; string message = new stri
13、ng2; byte bytes = new byte1024; string uid = ; IPEndPoint ie = (IPEndPoint)sktAccept.RemoteEndPoint; bool bRcvMsg = true; try while (bRcvMsg) Byte buffer = new Byte1024; /接收子画面传过来的内容 int rec=sktAccept.Receive(buffer); string msg = Encoding.ASCII.GetString(buffer, 0, rec); message = TransMessage(msg)
14、; bytes = Encoding.ASCII.GetBytes(msg.ToCharArray(); uid = message0.Trim().ToString(); if (message1 != null) string name = GetSqlconStr.getName(uid); if (message1.Trim() = ALL) /全发送 for (int i = 0; i ClientsList.Count; i+) SendMsgToClients(CList)ClientsListi, msg); /发送给单个客户端 else string myid = messa
15、ge1.Trim(); IPEndPoint iep = GetSqlconStr.getEndPoint(myid); for (int i = 0; i ClientsList.Count; i+) if (IPEndPoint)(CList)ClientsListi).Sockt.RemoteEndPoint).Equals(iep) SendMsgToClients(CList)ClientsListi, msg); break; else if (message2.ToString() = LoginIn) /Invoke改变主机登录名单(增加登录机到主机名单List) Change
16、Lb(uid, true); for (int i = 0; i ClientsList.Count; i+) SendMsgToClients(CList)ClientsListi, msg); /新建一个CList类对象,加入到服务器的客户端列表 CList client = new CList(message0, sktAccept.RemoteEndPoint, tdAccept, sktAccept); ClientsList.Add(client); /给新登录的客户端发送已登录人的名单 string userList = List|; for (int i = 0; i this
17、.lbUsers.Items.Count; i+) userList += lbUsers.Itemsi.ToString() + |; /buffer = Encoding.ASCII.GetBytes(userList.ToCharArray(); /给新登录的客户端发送登录人名单 SendMsgToClients(client, userList); SqlConnection con1 = new SqlConnection(GetSqlconStr.getConstring(); con1.Open(); string sql1 = insert into LoginStatus(U
18、serId,IpAddress,Port,LoginInTime,LoginOutTime) values( + uid + , + ie.Address.ToString() + , + ie.Port.ToString() + , + DateTime.Now.ToString() + ,null); SqlCommand cmd1 = new SqlCommand(sql1, con1); cmd1.ExecuteNonQuery(); con1.Close(); /退出 else if (message2.ToString() = LoginOut) SqlConnection con
19、2 = new SqlConnection(GetSqlconStr.getConstring(); con2.Open(); string sql2 = update LoginStatus set LoginOutTime= + DateTime.Now.ToString() + where UserId= + uid + and LoginOutTime is NULL; SqlCommand cmd2 = new SqlCommand(sql2, con2); cmd2.ExecuteNonQuery(); con2.Close(); SqlConnection con3 = new
20、SqlConnection(GetSqlconStr.getConstring(); con3.Open(); string sql3 = update Users set Status=离线 where UserId= + uid + ; SqlCommand cmd3 = new SqlCommand(sql3, con3); cmd3.ExecuteNonQuery(); con3.Close(); ChangeLb(uid, false); int nRemove = 0; for (int i = 0; i ClientsList.Count; i+) if (CList)Clien
21、tsListi).Sockt.RemoteEndPoint.ToString().Equals(sktAccept.RemoteEndPoint.ToString() nRemove = i; bRcvMsg = false; break; sktAccept.Close(); ClientsList.Remove(ClientsListnRemove); string strSendMsg = nRemove.ToString() + | + LoginOut; for (int i = 0; i ClientsList.Count; i+) buffer = Encoding.ASCII.
22、GetBytes(strSendMsg); /给新登录的客户端发送登录人名单 SendMsgToClients(CList)ClientsListi, strSendMsg); catch (Exception) ListenThread.Abort(); ServerSocket.Close(); AcceptThread.Abort(); (5)服务器向客户端发送在线好友名单: private void SendMsgToClients(CList client, string Msg) try byte bufferToClient = Encoding.ASCII.GetBytes(M
23、sg.ToCharArray(); client.Sockt.Send(bufferToClient); catch (Exception) finally delegate void changeLb(string str, bool bIsAdd); /访问主线程 private void ChangeLb(string str, bool bIsAdd) /invoke调用主线程 this.Invoke(new changeLb(ChangeLbText), str, bIsAdd); /设置主线程界面的值 private void ChangeLbText(string str, bo
24、ol bIsAdd) if (bIsAdd) lbUsers.Items.Add(str); else lbUsers.Items.Remove(str); (6)将收到的信息根据“|”分开,以便与判断信息类型: private string TransMessage(string tmsg) int sum = 0; string reMessage = new string3; char list = tmsg.ToCharArray(); for (int i = 0; i list.Length; i+) if (listi = | & sum 2) sum = sum + 1; el
25、se reMessagesum += Convert.ToString(listi); return reMessage; (7)运行服务器时的初始设置: private void Server_Load(object sender, EventArgs e) cmboxStatus.SelectedIndex = 0; this.btnOpen.Enabled = true; this.btnClose.Enabled = false; (8)信息刷新,以便于及时显示上线用户: private void Refresh(string sql, DataGridView dgw) SqlCon
26、nection conn = new SqlConnection(GetSqlconStr.getConstring(); conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); SqlDataAdapter da = new SqlDataAdapter(cmd); DataSet ds = new DataSet(Users); da.Fill(ds); dgw.DataSource = ds.Tables0; conn.Close(); (9)根据条件选择显示对应的用户信息: private void cmboxStatus_SelectedIndexChanged(object sender, EventArgs e) string sql = ; if (cmboxStatus.SelectedItem.ToString() = 在线) sql = select * from Users where status=在线; else if (cmboxStatus.Sel