JAVA课程设计聊天室系统

时间:2024.4.13

Java课程设计指导书(学生版初稿)

第二章聊天室系统

目标

 2.1 背景介绍

 2.1.1 业务背景

 2.1.2 技术背景

 2.2 需求分析

 2.2.1功能需求分析

 2.2.2 业务对象分析

 2.2.3 验收测试要求

 2.3 系统设计

 2.3.1 总体设计

 2.3.2 详细设计

 2.4 系统实现

 2.5 小结

 2.6 展望


第二章   聊天室系统

学习目标:

1、理解基于网络的C/S模式的软件系统结构,掌握网络编程的基本概念。
2、了解Java 的多线程机制,掌握Java多线程技术的应用。
3、熟练掌握基于TCP协议的Socket编程。
4、了解Socket编程的协议约定,掌握简单应用协议的开发。

5、进一步巩固发展团队协作能力。

学习寄语:想必大家都用过QQ,其主要功能就是聊天,是不是很想知道它是如何实现的?本项目就是帮你实现一个简单的聊天系统,当然跟商业项目没法比,但从中你却可以了解这些系统是如何实现的,学到开发类似系统的基础知识和基本技能(基本并不意味不实用)。本章的内容有一定难度(多线程、基于TCP的应用协议编程),所以系统的开发采用了“增量迭代”的开发方式,由简易到繁难,希望你能顺利前行。我们的信念依然是:“不抛弃,不放弃”。你的改变和收获依然是老师真诚的期待,期待你更踏实、更自信。Come on!


 2.1 背景介绍

 2.1.1 业务背景

随着网络社会的不断发展,具有相同兴趣的网民需要互相远程交流,既要能省钱又要能即时交互,电话太贵、email又嫌慢,所以开发一个类似QQ 的及时通讯系统就变得非常有意义了。“Happy Chat”聊天系统应运而生,它较之QQ的唯一好处是自主开发,用的放心,更适合在局域网内使用。它提供的功能远不如QQ丰富,但应具有如下功能:(1)与聊天室成员一起聊天;(2)可以与聊天室成员私聊;(3)用户注册、登录;(4)服务器监控聊天内容;(5)服务器发送通知;(6)服务器踢人;(7)保存服务器日志。(8)保存用户聊天信息。

 2.1.2 技术背景

 本系统要求使用java技术开发,使用文件保存数据,集成开发环境使用eclipse。开发者应有java程序设计语言、SWING基本GUI组件、多线程、文件使用、socket编程、使用eclipse的基本知识和技能。系统采用两层C/S体系结构,C端负责通过GUI与客户交互,实现注册、登陆、收发信息、退出等功能; S端是聊天系统的应用服务器,主要有处理用户注册、登录、用户收发信息、用户退出等功能。C端和S端是通过网络交互的,其基本原理如图1所示:

图1 C/S通讯基本原理图


首先服务器启动,它会建立一个专门用于接收客户端连接请求的“倾听Socket”(相当于总服务台,有固定的IP地址和端口号),然后等待客户的连接请求。

当用户想聊天时,从界面输入信息,然后与服务器建立Socket连接(连接时应指定服务器的IP地址和端口号,而客户端socket的端口由本方操作系统从空闲端口中确定),服务器端的“倾听Socket”收到连接请求后,一般会接受连接请求,并生成一个服务端socket(其端口号由服务端操作系统从空闲端口中确定),专门负责与此客户端socket的通信。一旦连接请求成功,客户端将信息及请求通过本方socket的输出流发送给服务器端相应的socket,服务端则通过服务器端Socket的输入流接受客户端传输过来的信息及请求,分析是何请求,然后根据请求类型,进行相应的处理(如登录、转发信息等)。服务方也可以根据需要,通过socket的输出流发信息和请求给客户端(公告)。客户方和服务方都可以通过关闭本方的socket而结束一次通讯过程。

不难发现服务器需要能同时接受多个客户的请求,为了实现这一点,一般使用多线程机制来处理,对每一个客户端连接通讯,服务器端都有一个线程专门负责处理(相当于一个服务员专门服务一个以IP地址和端口号唯一标识的客户)。

上述方式两个聊天者之间通信必须通过服务器进行转发,聊天者多时,显然服务器是个性能瓶颈。能不能聊天者之间直接通信?当然可以,这是所谓的P2P聊天室,缺点是对聊天者缺乏集中监管的手段。也有界于二者之间的,即有一服务器,接受注册和登录,实际聊天双方通信时,仍然是直接通信,此时服务器相当于一个婚姻介绍所,只管牵线搭桥,具体谈还是聊天者自己的事。

本文主要采用聊天信息通过服务器转发的方式,而且只支持一个聊天室。因为其他典型系统如电子邮件系统,FTP系统均采用类似结构,WEB服务系统本质上也是C/S系统,只不过其客户端是浏览器,采用了HTTP通信协议和HTML,所以变成了B/S结构,可以认为是C/S的一个具体应用,其机理是相似的。


2.2 需求分析

2.2.1功能需求分析

系统的主要功能已在业务分析中有所介绍,在这里需要对每个功能从使用者角度作较为具体的分析。很明显,整个系统的功能可以自然地分为客户端和服务器端。以下是主要用例描述

一 客户端

1 . 注册

(1)客户启动程序,显示出登陆界面

(2)客户选择其中的注册按钮,系统显示注册界面

(3)客户填写用户名、密码、确认密码、性别、年龄、电子邮件,按确定按钮

(4)系统验证密码和确认密码是否相符、用户名(不能重复)、电子邮件格式、年龄(大于10小于100)

(5)系统发送上述信息及“注册请求”到服务端,等待服务端返回“注册成功”消息

(6)系统提示注册成功

(7)系统返回登陆界面

若验证失败,提示“重新输入”

若服务端返回“注册失败”,提示“注册失败”

若服务端返回“注册失败  用户名重名”,则提示“注册失败 用户重名”。

2. 登录

(1)客户启动程序,显示出登陆界面

(2)客户填写用户名、密码,服务器IP地址,按登陆按钮

(3)系统验证用户名、密码,不能为空、密码字符长度为6-10

(4)系统发送用户名、密码及“登陆请求”到服务端,等待服务端返回“登录成功”消息

(5)若成功系统显示客户端主界面(收发消息界面)

 若用户名、密码验证失败,系统提示;“用户名或密码错”,重复3次若仍不能通过验证则客户端程序退出。

若服务端返回“登录失败”,系统提示“用户名或密码错”。

3. 发送信息

(1)在客户端主界面,用户输入消息,选择是群发还是私聊,若是私聊还要选择对方用户名,按发送按钮

(2)系统验证消息长度, 私聊要求目的方用户名非空。

(3)系统发送信息及“接收消息请求”到服务器端,等待服务端返回“接收成功”消息(等待返回消息可省)。

(4)系统提示信息已发送

若发送不成功,则系统提示“发送失败”。

4. 接收信息

(1)客户端系统启动,进入主界面后,会显示消息接收框

(2)其他客户或服务端系统本身发送消息过来,系统接收,分析确认是”

     接收消息请求“,则分析提取出消息

(3) 在消息接收框逐条显示发送者姓名、发送的消息。

5  退出

(1)用户请求退出,按退出按钮

(2)系统确认用户退出(对话框)

 (3)系统发“退出请求”到服务端,等待服务端返回“退出成功”(等待返回消息可省)

(4)客户端系统关闭连接,程序退出

二 服务器端

1. 用户注册

(1)系统启动后,等待客户请求

(2)客户请求到,接受请求,分析确认是“注册请求”

(3)系统读取信息,分析并再次验证用户名、密码、确认密码、性别、年龄、电子邮件。

(4)系统根据用户名,在已有客户记录中查询,确认没有重名

(5)系统将用户名、密码、确认密码、性别、年龄、电子邮件信息保存

(6)系统向客户端发送“注册成功”消息

(7)系统在监控界面上写信息:xx客户名  已注册 注册时间

若重名,向客户端发“注册重名”消息

若注册失败,向客户端发“注册失败”消息

2. 用户登录

(1)系统启动后,等待客户请求

(2)客户请求到,接受请求,分析确认是“登录请求”

(3)系统读取信息,验证用户名、密码是否存在

(4)系统验证是否已经登录

(5)系统验证用户是否已超过最大用户数

(6) 系统将客户加入聊天室,通知其它客户“新用户加入”

(7)系统向客户端发送“登录成功”消息

(8)系统在监控界面上写信息:客户名:已登录 登录时间

若验证失败,向客户端发“验证失败”消息

3. 发送信息(用于管理员向聊天者发送公告信息)

(1)系统启动后,等待管理员请求

(2)管理员在监控界面输入消息,确定发送类型(群发还是私聊),若私聊还需指定目的用户名,按发送按钮

(3)系统读取信息,分析并确认是群发还是私聊

(4)若是群发,则将信息发给聊天室内其它所有用户;若是私聊,则将消息发给指定的用户。

(5)系统在监控界面上写信息:管理员--〉消息 

若出现异常,提示“发送失败”。

4  接收信息

(1)系统启动后,等待客户请求

(2)客户请求到,接受请求,分析确认是“接收信息请求”

(3)系统读取信息,分析并确认是群发还是私聊

(4)若是群发,则将信息发给聊天室内其它所有用户

(5)系统向客户端发送“接收成功”消息(可省)

(6)系统在监控界面上写信息:xx客户名--〉消息  群发/私聊

若出现异常,向客户端发“接收失败”消息(可省)

5         用户退出

(1)系统启动后,等待客户请求

(2)客户请求到,接受请求,分析确认是“用户退出请求”

    (3)系统从聊天室删除客户,并通知其它用户“客户退出”

    (4)系统向客户端发“退出成功”(可省),关闭连接。

聊天系统主要应用在局域网,在性能方面要求,客户到客户间消息传输时间不大于5s,传输可靠性要求95%的情况下,消息可以可靠传到目的地,同时在线用户数量不小于200。

友情提示:本需求分析只是分析了主要的用户使用情况,在对客户端进行分析时,聊天者是一个接口,服务方也是一个接口,在对服务端进行分析时,客户端是一个接口,服务方管理员是另一个接口。在接口处只关心接口处的输入输出,并不关心接口里面是如何实现的。例如客户端登陆用例分析时,客户发“登录请求”信息到服务端,此时只需关注服务方返回情况,所以只需等待获取服务端返回信息,而无需知道服务方是如何处理的。服务方如何处理是在服务端登录用例中描述的。

隐含需求的发现。在登录用例中,客户和服务方都有登录用例,合作完成一个完整的登录过程,密码和用户名的验证一般都能想到,但限制用户数量,同一用户不能重复登录则不太容易想到,这里经验就会起作用,这是第一步,想到了就要向客户提出(必要时解释),具体要不要做由客户决定,则是第二步。这里还有一个度的把握,因为不可能任何情况都要向客户解释。经验如何来?学习前人的经验并运用到自己的项目开发中,体会转化为自己的东西应该是个有效途径。

优先级的划分。作为聊天系统,其主要功能就是聊天,客户间互相发送消息,所以优先级最高的是客户端的发送、接收、退出用例,服务器端的接收、退出用例。注册、登录用例次之。而公告、踢人、保存日志及聊天记录、个性化界面等又次之。优先级划分后,设计实现时,先做优先级高的,例如本系统,为了实现上述全部功能,可以分3次迭代,第一次,实现高优先级的基本聊天功能,第二次实现注册和登录,第三次实现公告、踢人、保存日志及聊天记录等。其核心是先实现最重要(用户观点)和最难的(开发者观点),这一观点和日常思维相反,但却是有效避免风险和损失的方法,因为必需的和难度大的一开始就实现不了的话,项目可以及时改变,否则等到最后才发现了难以解决而又必须解决的问题时,损失就会更大。当然在攻克重难点时,又可以采用由易到难的办法,逐步解决。一个是策略,一个是战术,思路相反,关键是要运用得当。

2.2.2业务对象分析

从上述的分析中,运用名词法,可以发现出主要的业务对象:

1 聊天者:

属性:用户名、密码、确认密码、性别、年龄、电子邮件

  行为:登录、注册

2    聊天客户端:

属性:消息、聊天者、界面

行为:接收处理,发送处理、退出

3    消息

属性:消息类型、消息参数

行为:创建消息、获取消息类型、获取消息参数

4  服务器(相当于总服务台)

   属性:IP、端口、服务监控、消息处理者

   行为:监听、创建消息处理者、创建服务监控

    5  服务监控

       属性:服务状态,消息,聊天者列表、界面

       行为:发送服务方消息、关闭服务器

6        消息处理者(相当于服务员)

属性:连接(socket)、消息

行为:处理消息(登录、注册、发送、接收、退出),收发消息

友情提示:业务对象是系统中对象的初步提炼,其属性和行为在后面还会修改、变动,主要与业务相关,和具体的实现关系不大,是后面设计中业务类的基础,但实际类的数量也许会很多,因为在设计甚至在实现时都可能需要建立新的类以实现功能。不少讲分析设计的书并不提到这一步,而是在设计中直接给出类图,结果是一样的,但反映不出这些类是如何来的,如果你注重过程,可以看一下本节,若果你注重结果,直接看设计实现中的相关内容。

2.2.3验收测试要求

测试环境:

客户及服务器机操作系统:Window XP,内存:512M。

客户端程序安装在客户机(至少2台)上,通过以太网(网速至少10M)与服务器相连。

前置条件:

1 注册文件已创建但为空。

2 客户及服务程序安装配置正确,能正常启动运行。

3 客户程序与服务程序能通过网络互通。

一 初始化数据

1 客户端启动,进入注册界面,输入正确的注册数据,请求注册,查看服务端,看是否正确注册。

2 重复1,在另一客户端注册用户,注意不要重名。

二 功能测试

1 注册测试。(1)测试重名注册。进入注册界面输入重名用户名,其它正确,请求注册。(2)测试空输入,进入注册界面,直接按注册按钮。(3)测试口令的一致性,口令长度,年龄及邮箱的数据有效性(具体测试用例设计可参考需求分析及详细设计中的相关部分)。

2 登录测试。(1)输入正确的口令和密码,按登录按钮(2)空输入,直接按登录按钮。(3)分别输入用户名不正确但密码正确,用户名正确但密码不正确,用户名和密码均不正确,应均不能正确登录。(4)以同一用户名重复登录一次

测试(1)(3)时要查看服务端的显示

3 发送接收测试。(1)进入收发界面,群发一条消息,观察其它客户是否收到消息,察看服务器有无相应显示 (2)私聊一条消息,察看指定用户是否收到消息

(3)无任何输入,直接按发送。(4)退出系统,察看服务端显示,察看其它客户端是否已将该客户名删除。(5)再启动客户端,登录进入收发界面,连续群发(至少3次),连续私聊(至少3次),再连续群发,观察其它客户及服务方的显示是否正确。

三 可靠性测试

 1 切断一客户至服务器的网络连接,分别进行注册、登录消息,客户端应能给出提示,而不是死机或退出,在正常聊天过程中,切断一客户端网络连接,客户程序应能给出提示。再接通网路,继续发送信息,应能正常运行。至少关闭并重启程序后,应能正常收发。同时观察其它客户及服务器收发、客户列表是否正常。

2  在正常收发中,强行关闭服务器,观察各客户端的反应。客户端应给出发送异常提示,不应退出或死机。

四 性能测试(可选)

编制一测试程序,作为客户端,登录进系统,向服务器按指定时间间隔群发消息。可同时启动多个发送线程(模拟多个客户),同时向服务器群发消息。看在200个模拟客户(200个发送线程),每1s一个消息的情况下,服务器能否满足客户到客户传输时间小于5s的要求。也可以考虑使用JMeter压力测试工具。


 2.3 系统设计

 2.3.1 总体设计

一 系统总体结构

      总体设计阶段主要是确定系统的体系结构和主要模块 ,显然系统分客户端子系统和服务器子系统。系统体系结构如图2所示:

 图2 系统体系结构图

客户端可以划分成三子层,服务端也可以划分出三个子层,客户和服务器间通信采用的是可靠的TCP协议。基本的聊天过程如下:

  0 客户端启动注册、登录后,进入收发界面,此时C/S连接已建立,C处于接收状态。

  1 客户A从界面输入消息,确定群发,业务逻辑层从界面获取信息并验证后生成“消息接收请求”消息,再将消息作为参数调用网络通信层的发送函数,发送函数将消息发往服务器,然后等待服务器的消息

 2  服务器收到消息,确定是客户A发来的,从消息中分析出是群发,然后从当前客户列表中取出除A以外的与每个客户(B,C)对应的socket,然后通过socket将消息转发给客户B,C。

 3  服务器在监控界面上显示:客户A—〉消息  群发

 4 服务器生成“消息接收成功”消息,向客户A回发(可省)。

 5 客户A收到消息,确定是“消息接收成功”消息后,在界面上显示发送成功。

 6 客户B/C的通讯模块接收到消息,分析确认是“消息接收请求”,则在界面上显示:客户A-〉消息 群发。不向服务器发送消息收到的确认消息。

消息收发的简图如下图3所示:

                       图3  消息收发示意图

经验共享:下面是本人在网络编程方面多年积累的经验,感兴趣的可以读一读。

要不要回复消息?由于使用的是TCP协议,可以保证点对点的可靠传输,所以最简单的情况是无需回复,在上图中取消3也可以,客户A将消息发到服务器就认为已成功群发了消息,但若客户B、C都断线,显然消息并没有成功群发。最复杂的是客户B、C收到消息后也回复给服务器,服务器确认都收到回复后再向A回复。前者编程简单、性能好,但对于某些异常情况不能可靠处理,后者编程复杂,性能差,但可靠性高。这就要根据情况在上述矛盾中折中,对于“聊天室系统”,对可靠性要求并不高,即使消息有5%未收到,也没有大的问题,另外聊天者是处理各种不可靠问题的最佳人选,实在不行,可由人重启系统(不会带来任何人身及财物损失),另外本系统主要应用在局域网,而局域网的可靠性是较高的。所以本文推荐采用无回复方式。这适用于其它消息的处理。

消息的收发方式?主要分两种,一种是推方式,例如客户A有了消息就直接发给服务器,这是推方式,即由数据源方直接将消息发给接收方;另一种是拉方式,即接收方主动向数据源请求获取数据,例如服务器通过定期询问客户A有无数据的方式,客户A一旦有消息,就会发消息给服务器。显然这两种方式应用的场合是不一样的,上文中描述的是“推”方式,客户A将消息发(推)给服务器,服务器将消息再转发(推)给其它客户。数据源(客户A)的特点是消息的产生是不确定的(接收方难以知道发方何时有消息),产生就发是高效的方式,若服务器采用轮询的方式,一方面在大多数情况下会产生许多无谓的询问,另一方面会降低服务器的性能(特别是服务器可能成为整个系统的性能瓶颈,这就尤为重要),这里还隐含了一个假设:在聊天过程中,服务器一直等待消息的到来,B、C也随时能接受消息,所以发送(推)一般是能成功的。服务器向客户B,C推消息(相当于客户B、C接收消息),是基于B、C都在准备接收的假设,这一点较之服务器一直可靠运行的假设,其假设的有效性较低,因为客户方的行为难以预料,可能客户重启了系统,可能突然断电等。服务器可能白发了消息(为保证消息发到客户,服务器会重复发送消息,造成性能损失),因为它不知道客户是否在等着接收消息,这时可以考虑客户方主动接收消息,通过定期轮询的方式,向服务器要数据,这时服务器就知道客户一定能接收消息,所以就发消息给客户,这种方式是“拉”方式。此方式较之服务器直接推,服务器性能上有所损失,主要用于处理客户的轮询消息(较之重发消息,开支一般较小),客户方因为要轮讯,也损失性能,但这对于客户端并不重要(其计算能力绝大部分情况下都足够),编码量上C/S均增加了,它的好处是如果多数情况下不能保证客户方一直在线,则服务方可以处理多次轮询,但只发一次消息给客户,较之不断的重发消息给客户(尤其是大量用户群发时)总体性能上是提高的。轮询时间的确定主要是在消息收发的及时性和性能影响之间取得平衡,周期过小,服务器处理压力大,周期过长,客户不能及时收到消息,一般可取2—5秒。

 推荐的方式是“推”方式,因为“Happy Chat”应用于局域网,网络条件较好,且基于可靠的TCP协议传输消息,本身对可靠性要求也不高,具体说就是服务器向客户转发消息时,不管成功与否只发一次,客户向服务器发消息时,若出错,不会自动重发,但会给出提示,由聊天者决定是否重发(如再次按发送按钮)。即认为在多客户聊天过程中服务器一直正常运行,客户端也一直正常。这样做以后,简化了设计编码,性能也得到提高。

结论:系统采用无回复的“推”方式收发消息。

二  服务器结构

    服务器端主要的模块和结构如图4所示:

 

       AppServer.java    为服务器端监听类,负责服务器的启动,包括启动监听端口、服务器监控界面。

       ServerFrame.java  为服务器监控窗体,负责监控服务器运行状态,聊天内容,发送公告,踢人……

       Connection.java  为服务器连接处理的具体实现。

       WordFilter.java     语言过滤类,处理非法聊天内容的过滤。(可选)

三 客户端结构

客户端主要模块和结构如图5所示:

       ChatClient.java 为客户端程序启动类,负责客户端的启动和退出。

       Login.java 为客户端程序登录界面,负责用户帐号信息的验证与反馈。

       Register.java 为客户端程序注册界面,负责用户帐号信息的注册验证与反馈。

       ChatRoom.java 为客户端程序聊天室主界面,负责接收、发送聊天内容与服务器端的Connection.java 亲密合作。

       Windowclose 为ChatRoom.java的内部类,负责监听聊天室界面的操作,当用户退出时返回给服务器信息。

       Clock.java 为客户端程序的一个小程序,实现的一个石英钟功能。(可选)

2.3.2详细设计

2.3.2.1 客户端设计

 注册模块

程序将通过Register.java用户注册界面收集用户的用户名、密码、年龄、电子邮箱等。参考界面如图6。

图6  客户端注册界面

处理过程:当用户注册时,客户端的Register.java界面收集并验证了用户的注册信息后,封装成Register_Customer类然后通过建立在Socket的连接之上的对象输出流将用户注册信息发送给服务器端,服务器端将请求转发给Connection处理,Connection 在收到信息后将验证数据的完整性并在数据库中查找该用户名是否已经注册,然后将注册信息加入数据库,最后将注册结果返回给客户端。客户端显示结果(重名、成功、失败),并断开连接。

验证要求如下:

1 验证用户名是否为空

即用户名的字符长度不为0,如果验证通过,就继续下面验证,否则返回“用户名为空”错误。

2   验证密码是否为空

即密码的字符长度不为0,如果验证通过,就继续下面验证,否则返回“用户密码为空”错误。

3  验证密码的一致性

验证密码两次输入是否一致,如果验证通过,就继续下面验证,否则返回“密码两次输入不一致,请重新输入”错误。

4  验证年龄是否为空

即用户年龄的字符长度不为0,如果验证通过,就继续下面验证,否则返回“用户名为空错误”

5  验证年龄的合法性

即用户年龄介于10到100之间,如果验证通过,就进行继续下面验证,否则返回“用户年龄为空”

6  验证电子邮箱的合法性

即电子邮箱字符串必须有“@” ,如果验证通过,就进行继续下面验证,否则返回“电子邮箱不合法”,

二  登录模块

程序将通过Login.java用户登录界面收集用户的用户名、密码,如图7所示。

图7  客户端登录界面

登录过程如下:

当用户登录时,客户端由Login.java 界面收集并验证用户登录信息后,封装成Customer对象类然后通过建立在Socket的连接之上的对象输出流将用户登录信息发送给服务器端,服务器端将请求转发给Connection处理,Connection 在收到信息后将验证数据的完整性并在对象型数据库中查找该用户名是否已经注册,然后将注册用户的信息与登录请求信息进行密码验证,在登录成功后将该用户添加到在线用户列表,最后将登录结果返回给客户端。如果登录成功,客户端将继续启动聊天室主界面。

验证要求:

1     验证用户名是否为空

即用户名的字符长度不为0,如果验证通过,就进行继续下面验证,否则返回“用户名为空”错误,

2    验证密码是否为空

即密码的字符长度不为0,如果验证通过,就进行继续下面验证,否则返回“用户密码为空”错误

       当以上信息验证成功时,客户端将打开与服务之间的Socket连接,用对象输出流包装后将用户登录的信息发送给服务器端,并接收服务器处理完用户登录的信息。

       当客户端收到服务器返回的信息时,将会用信息对话框的形式告知用户是否注册成功,若成功则显示收发主界面ChatRoom,关闭Socket连接。

三 收发模块

当用户登录成功后,用户的登录界面将会消失,然后创建用户聊天室窗口(如图),在界面的标题栏将会显示当前登录用户的用户名,以防止一个机器上的用户开多个帐号进入聊天室后分不清哪个窗口是哪个用户登录的,界面第一行将显示当前服务器的在线人数。位于界面正中的两个控件分别是List和TextBox用来显示当前服务器上的在线会员名单列表和公共聊天信息。

位于界面下方的就是一些用于聊天的功能控件和聊天内容个性化配置控件(可选)。包括聊天对象、聊天语气、聊天内容、聊天字体、风格、大小、颜色……

1 用户发送信息

当用户需要发送聊天信息时,可以在在线列表中选中聊天对象或者“所有人”,选择发言的语气,和是否私聊就可以发送聊天信息了。

当用户点击完发送按钮后,程序开始将当前用户名、聊天对象、聊天内容、聊天语气和是否私聊进行封装,然后获取Socket连接,再用对象输出流包装Socket的输出流将聊天信息对象发送给服务器端。

2 接收聊天信息

接收用户的聊天信息是用一个单独的接收线程实现的,因为客户端必须随时接收服务器发来的消息,而读取消息的方法采用的一般是读直到有消息到来的“阻塞读”方式,若在事件处理程序中读(占用的是事件处理线程)则会使程序在此后无法响应用户操作,所以需要生成一个单独的线程专门用于读取处理消息。而发送消息的方法则是将消息放入操作系统的发送缓冲区后就返回的,所以发送执行的相当快,可以在事件处理线程中直接调用。发送和接收可以同时进行。

当每次用户接收到聊天信息后将会开始分析聊天信息然后将适合自己的信息显示在聊天信息界面上。收发界面如图8所示。

图8  收发界面

图上的字体(个性化)、时钟、保存等是可选的功能,聊天者列表、聊天信息显示,聊天内容、私聊等是必需的。


2.3.2.2 服务端设计

功能描述:

1.接受用户注册信息并保存在一个基于文件的对象型数据库。

2.能够允许注册过的用户登陆聊天界面并可以聊天。

3.能够接受私聊信息并发送给特定的用户。

4.服务器运行在自定义的端口上1888。

5.服务器监控用户列表和用户聊天信息(私聊除外)。

6.服务器踢人,发送通知(可选)。

7.服务器保存日志(可选)。

一  用户注册

1.去数据库读数据

首先创建一个File文件类,载入“user.txt”,再用对象输入流ObjectInputStream包装File文件类,将存储在“user.txt”得向量对象Vector中载入内存中,现在系统中的向量就是对象型数据库,存储着每条用户信息对象。

2.判断是否是第一个注册用户

检测内存中的数据库对象集的当前容量是否为空,如果是,将收集的注册用户信息对象添加到数据库对象集中,然后直接写入数据库,否则继续进行其他判断。

3.判断用户名是否已经存在

检测内存中的数据库对象集的中有没有和当前收集的注册用户名相同的记录,如果有,就退出检测,否则继续下一个检测。

4.判断用户名是否为系统关键字

检测当前收集的用户名是否为“所有人”,如果是,就退出检测,否则继续下一个检测。因为“所有人”这个名字会和系统发生冲突,用户发给所有人的信息将会使用“所有人”这个特殊用户名。

5.用户名有效后 写入数据库

当前面的验证全部都通过的时候,将用户的注册信息对象添加到用户数据库中,然后创建文件输出流,再用对象输出流包装后写入本地文件将其持久化。最后关闭对象输出流和文件输入流。

6.给客户端返回信息

将用户注册成功或失败的信息用打印流包装Socket以后,输出给客户端注册结果的详细信息。

7 在监管界面上显示信息,如图9所示。

8.关闭Socket连接

当以上的程序都正常运行后,需要关闭Socket连接,否则将会浪费服务器与客户端之间的资源。

二  用户登录

1.去数据库读用户数据

打开用户数据库文件“user.txt”,将用户数据对象集载入内存,以供下面的程序使用。

2.验证用户名是否存在

在对象数据库中查找是否有与登陆用户的用户名相同的记录,如果有继续验证,否则退出验证处理,返回“没有此用户”错误。

3.验证用户密码是否正确

如果有与登录用户的用户名相同的记录,接着判断密码是否正确。如果密码正确,继续验证,否则退出验证处理,返回“用户密码错误”。

4.验证用户是否已经登录

在服务器的已登录用户列表中查找是否有该用户,如果有该用户,退出验证处理返回“该用户已经登录”错误。否则继续下面的验证。

5.验证是否已经超过最大登录人数

检测服务器的在线人数是否已经达到限制的最大人数,如果是,退出验证处理返回“当前服务器人数已满,请稍后再试”错误。否则继续下面的验证。

6.返回客户端信息

如果以上验证全部正确,就返回客户端“用户登录成功”。

7 在监控界面显示信息,如图9所示

8.关闭Socket连接


当以上的程序都正常运行后,需要关闭Socket连接,否则将会浪费服务器与

                  图9 服务器管理界面

客户端之间的资源。

三  用户退出

1.接收退出信息

当用户退出时,客户端将会用基于Socket的对象输出流发给服务器退出对象。

2.在线列表中删除用户

用户退出后应该把用户从在线列表中删除,否则用户退出用户还在在线列表中,那么该用户下次将会无法登录。

3.更新在线列表

用户退出后将服务器端监控界面的用户列表更新。否则用户数据将会不同步。

                  图10  服务器用户信息管理界面

四 接收转发用户聊天信息

1.接收用户聊天信息

当用户发送聊天信息时,服务端将会收到客户端用Socket传输过来的聊天信息对象,然后将其强制转换为Message对象。

2.过滤用户聊天内容的非法信息(可选)

分析聊天信息对象的聊天内容,用语言过滤类将非法字符过滤掉。语言过滤的时候将会打开“badword.txt”文件,不允许出现的关键词将会存储在文件中,关键字之间以逗号分割。当聊天内容中出现要过滤的关键字是 将会被系统屏蔽,而聊天内容也将替换成“非法内容,系统屏蔽”。
                 

3 返回聊天信息

   然后系统从Message中提出消息类型,再根据类型将消息强制转化ChatMessage,调用ChatMeaage中方法,获取目的客户名称列表,根据目的地用户名从用户名、socket键值表中获取socket,再通过socket将Message转发到目的客户。

Message定义如下:

1消息类型

  接收消息请求,登录请求,登录响应,注册请求,注册响应

  2 获取类型方法getType

ChatMessage继承自Message,增加如下属性:(1).目的用户名列表


(2). 用户聊天内容;相应的还有LoginMessage、LoginACKMessage、RegisterMessage,RegisterACKMessage. LogoutMessage(客户退出)等

                   图11  通信协议交互图

友情提示:服务端采用一个“监听线程”负责监听连接请求,收到连接请求则创建socket及相应的处理线程,该线程在一个循环中通过socket读取消息,分析处理消息,直到遇到“退出”消息或管理员命令,线程才退出。所以服务端是“单线程监听,多线程处理”的模式。

经验共享:具体设计时应用协议的设计是难点,要根据需要确定具体的消息种类,分清客户端发出哪些消息,接收哪些消息,服务端又发出哪些消息,接收哪些消息。然后将这些消息构成一个完整的聊天业务过程。可以用图11描述。整个过程分注册,登录,收发,退出四个阶段。其中LogoutMsg为客户声明自己离线,其它客户收到后将该用户信息删除,而QuitMsg是服务方发出的要求客户离线的消息,收到的客户一般需要关闭连接。

协议格式的设计也很重要,有基于文本格式和二进制格式两类。文本格式的协议有HTTP,SMTP(简单电子邮件传输协议)等,二进制的协议如java 的RMI、CORBA,windows的DCOM等。文本形式的协议简单,可跨平台,例如使用HTTP协议的WEB应用,客户端可以是Window,服务器端可以是Linux,Web服务器既可以是用C开发的Apache,也可以是用其它语言开发的,只要他们遵守协议。其缺点是性能不好,特别是传输大信息量消息时,其次是名文传输,很容易被破解。二进制方式的特点和文本方式相反,缺点是难以实现不同平台不同语言间的传输,优点是高效,有一定的保密性。对于聊天室系统,若用文本格式传输,建议采用以下格式: 消息类型+分隔符+参数1+参数分隔符+参数2+参数分隔符+ …

分隔符可用‘:’,并保证‘:’不出现在参数及消息类型字符串中,参数分隔符可用‘\0’,也需要保证其不出现在参数及消息类型字符串中。若采用二进制形式,直接将相关消息对象序列化后即可。


2.4 系统实现

2.4.1客户端实现

 客户端实现的关键在于接收线程的实现,部分原型参考代码如下:

class ReadMessageThread extends Thread{

    public void run(){

      String line="";

//循环读并处理消息

      while(true){

        try{

          line=cin.readLine();

        }catch(IOException ex){

          System.out.println("输入输出异常\n");

        }

//提取消息中的消息类型

        StringTokenizer st=new StringTokenizer(line,":");

        String keyword=st.nextToken();

//根据类型处理消息

        if(keyword.equalsIgnoreCase("quit")){ //服务方指令退出

          try{

            socket.close();

            jTextArea1.append("接收到服务器同意端口信息,套节字关闭\n");

            break; //jump out of read thread

          }catch(Exception ex){

            jTextArea1.append("关闭套接字异常");

          }

        }//新用户加入

        else if(keyword.equalsIgnoreCase("Login")){

          Vector imessage=new Vector();

          while(st.hasMoreTokens()){

            imessage.addElement(st.nextToken());

          }

          jList1.setListData(imessage);

        }//接收的消息

        else if(keyword.equalsIgnoreCase("AcceptMsg")){

          String message=st.nextToken("\0");

          message=message.substring(1);

          jTextArea1.append(message+"\n");

        }

      }

    }

}

}

注:本例中通信协议的格式,为文本格式,基本格式为:类型:用户名/用户消息,较之设计中使用的二进制消息对象形式,好处是简单、可读,可移植性好,即使C/S采用不同语言开发,只要遵守此文本格式协议,都可以互相通信,HTTP协议就是这方面的例子。

2.4.2服务器实现

服务器端的关键在于接收处理线程和监听线程,基本代码如下:

//监听线程,实现多客户聊天,解决只能处理单客户问题

  class ConnectSocket extends Thread{

    Socket socket;

    public void run(){

     while(true){

       try {

         socket = serverSocket.accept();//接收连接,返回连接socket

       }

       catch (IOException ex) {

         jTextArea2.append("创建套接字连接错误\n");

       }

       if(socket!=null){

         try{

           Client client=new Client(socket);//创建客户处理线程

           clients.addElement(client);//clients是Vector,存储客户对象

           if(checkName(client)){ //在clients中判断客户是否存在

             client.start();//启动处理线程

             notifyRoom();//通知其它客户,有客户加入

           }

           else{

             disconnect(client);

           }

         }catch(Exception ex){

           jTextArea2.append("信息读取错误!\n");

         }

       }

     }

    }

  }

}

//使用客户处理线程来实现信息的读取,解决程序死锁的问题

  class Client extends Thread{

    String name;

    BufferedReader in;

    PrintStream out;

    Socket socket;

    public Client(Socket client){

      this.socket=client;

      try{

        in=new BufferedReader(new InputStreamReader(socket.getInputStream()));

        out=new PrintStream(socket.getOutputStream());

        String info=in.readLine();

        StringTokenizer stinfo=new StringTokenizer(info,":");

        String head=stinfo.nextToken();

        name=stinfo.nextToken();

        jTextArea1.append("系统消息:"+name+"已经连接\n");

      }catch(IOException ex){

        System.out.println("创建网络输入输出流错误!");

      }

    }

//发送方法

    public void send(String msg){

      out.println(msg);

      out.flush();

    }

//线程运行部分

    public void run(){

      String str="";

      while(true){

        try{

          str=in.readLine();//读消息

        }catch(IOException ex){

          jTextArea2.append("输入输出异常\n");

          disconnect(this);

          notifyRoom();

          return;

        }//解析、处理消息

        StringTokenizer st=new StringTokenizer(str,":");

        String keyword=st.nextToken();

        if(keyword.equals("Logout")){//客户退出请求

          try{

            send("Logout");

            notifyRoom();

            this.stop();

            jTextArea1.append("连接关闭");

          }catch(Exception ex){

            jTextArea2.append("套接字关闭异常\n");

          }

          break;

        }

        else if(keyword.equals("AcceptMsg")){//客户消息

            String command=st.nextToken();

            if(command.equalsIgnoreCase("BOARD")){//实现信息的群发

              String message="";

              message="AcceptMsg:"+name+":"+st.nextToken();

              sendClients(message);

            }

            else{//将信息发给某个客户

              String message = "AcceptMsg:"+ name+":"+ st.nextToken();

              sendToClient(message,command);

            }

        }

      }

    }

  }

//实现群发,通知有客户加入

public void notifyRoom(){

    String people="Login";

    for(int i=0;i<clients.size();i++){

      Client c=(Client)clients.elementAt(i);

      people+=":"+c.name;

    }

    sendClients(people);

  }

//实现单发,私聊

public void sendToClient(String msg,String name){

    for(int i=0;i<clients.size();i++){

      Client c=(Client)clients.elementAt(i);

      if(c.name.equals(name)){

        c.send(msg);

      }

    }

  }

//实现群发消息

  public void sendClients(String msg){

    for(int i=0;i<clients.size();i++){

      Client c=(Client)clients.elementAt(i);

      c.send(msg);

    }

  }


 2.5 小结

    本项目是基于C/S结构的网络开发,具有一定的典型性,涉及到的C/S多线程编程模式,应用协议的设计,收发信息的方式是类似程序必须考虑的问题。 网络编程实际上是相当有难度的事,通过使用Java的网络类库,虽然大大简化了网络编程,但网络的随机性错误、时延、并发存取、多平台的固有属性, 使得网络开发要想在可靠性、性能、可扩展性、可移植性方面取得很好的平衡,变得极具挑战性。不管怎样,通过这个简单的聊天系统,学到了Java网络类的使用、多线程收发模式、简单网聊协议的设计实现及必要的可靠性处理。为了将所学的知识内化成自己的东西,需要你写一份小结,写下你所做的工作,发现了哪些问题?如何解决的?有哪些经验和教训?又产生了哪些问题?不应局限于技术,只要是与项目相关的属于自己的思考和想法都可以写下来,请牢记,你的意见(教学内容、教学方式方法、考核方式、解决问题的方式等方面)是对老师最好的奖励,经过深思熟虑的宝贵意见还将是你在本项目中最终取胜的法宝。

2.6 展望

本系统是一个简单的网聊系统,无论在功能上还是易用性、可靠性等方面,与现实中的QQ系统相比仍有相当大的差距,但本系统可进一步扩展成实用的系统,形成的代码可以应用到以后类似系统的开发中,所以这些代码值得保存。C/S网络应用主要在局域网中存在,具有快速、高效、安全性好等特点,缺点是客户端程序的维护十分困难,试想若有数以万计的用户,程序功能一旦变化,就要全部重新安装配置客户端程序,代价太高,于是B/S方式应运而生,其在internet/intranet获得广泛应用,最大的特点是程序的维护只在服务器端,客户端只需使用标准浏览器即可,其次可以支持多平台、易使用。但其在性能、安全性方面不如传统C/S,所以在性能、安全要求高的场合C/S还有一席之地(如工业测控、教学监控等领域)。当然还有其它的通信方式如P2P,B/S方式本身也在向面向服务的应用发展。不管如何发展,绝大多数技术都是基于原有技术发展而来,因此可以说本项目中涉及的技术也是上述各种新技术的基础。

最后以一句话结束本项目:“雄关漫道真如铁,而今迈步从头越!”。

参考资料:

1 AI Williams著,何雄译.Java网络协议内幕[M].中国水利水电出版社

2 陈敏,Java聊天室系统.论文。

3 深圳职业技术学院精品课程网,http://jpkc.szpt.edu.cn

附录1

“网吧计费管理系统“课程设计任务书(参考)

一.任务描述:

“海之星”网吧需要一个简易的网吧计费系统,需完成以下功能:

1.上机功能。输入卡号和密码上机,卡号或密码不对,不能上机,卡余额不足不能上机,正在使用的机器不能被再使用,不能一卡多用。上机需要记录卡号、机器号、上机时间。

2.下机时需要记录下机时间、发生费用和自动从卡里扣款,使该机器能再被使用;下机时,需要向用户显示本次上机的详细信息

   3.能支持发新卡、删旧卡、给卡充值,浏览卡信息;能增减机器及浏览机器信息;能根据时段设置费率。

   4.能统计每天、每月、每年的收费及上网人次。

   5.支持超时超费报警

6. 有使用帮助

 7. 程序使用前需要输入口令和密码

二.验收要求

1.资料完整:课程设计报告,源代码,数据库,可执行程序,安装配置及使用说明。

2.代码:功能完整正确,风格良好,有可靠性设计

3  数据库:表定义完整正确,数据存取正确

4  界面:   风格良好,有效性验证,在线帮助

5  通过验收测试(见课程设计指导书)。

更多相关推荐:
学生选课管理系统课程设计报告(更改)222

湖南农业大学研究生课程论文学院年级专业姓名学号课程论文题目课程名称评阅成绩评阅意见成绩评定教师签名日期年月日1目录一前言311开发背景312系统配置3二需求分析421系统总体功能分析422系统功能要求设计523...

C语言课程设计报告 学生选课系统加源代码

实践教学兰州理工大学计算机与通信学院20##年春季学期C程序设计课程设计题目:学生选课系统专业班级:通信工程1班姓名:学号:指导教师:成绩:摘要学生选课系统是一个教育单位不可缺少的重要系统组成部分,它对于学校的…

C++课程设计报告样例 学生选课系统设计报告

计算机与信息科学系学生选课系统设计报告班级专业题目指导教师学号姓名同组人C计科系08级2班计算机科学与技术学生选课系统无20xx年1月1日学生选课系统设计报告目录Contents引言3一设计目的3二总体设计4三...

C语言课程设计实践报告——《学生选课管理系统》[内附程序源代码]

C语言大型作业实践报告计算机1001制作者:20##年6月题目:选修课程管理系统要求:假定有N门选修课,每门课有课程编号、名称、课程性质、总学时、授课学时、实验或上机学时、学分、开课学期等信息。学生可按要求(如…

学生选课系统设计报告

学生选课管理系统设计报告班级计本1102学号323335组员张静静白雪杨月负责人白雪指导老师李微目录1引言错误未定义书签11设计目标错误未定义书签12采取的策略错误未定义书签13设计依据错误未定义书签14参考资...

学生选课信息系统数据库课程设计报告

Oracle数据库应用开发期末设计报告学号20xx051341姓名秦小梅班级中软lt3gt班学期第二学期成绩数据库原理与应用课程设计目录一系统概述2二需求说明21需求调研22系统功能的设计和划分33数据流图44...

学生选课系统概要设计报告

学生选课系统概要设计报告班级网络102班姓名张宇飞08103639王籽硕081036311引言11编写目的学生选课系统可对学生信息选修课程科目成绩进行各方面的管理为用户提供充足的信息和快捷的查询修改手段选课管理...

学生学籍管理系统(数据结构课程设计报告)

数据结构课程设计专业班级姓名指导教师20xx年12月30日1课程设计的目的和意义一般来讲课程设计教学实验复杂一些深度更广并且更加接近实用目的是通过课程设计的综合训练培养学生实际分析问题编程和动手能力使学生对数据...

学生选课系统设计报告 软件工程课程设计

软件工程课程设计学生选课管理系统开发报告作者20xx2420xx霍海洋1引言311编写目的312背景313参考资料32任务概述321任务目标322用户的特点423假定和约束4231进度约束4232系统约束423...

学生选课管理系统软件 课程设计报告

山东交通学院面向对象课程设计学生选课管理系统软件院系别信息工程系班级计算091学号xxxxxxxxxxxx姓名xxxxx指导教师xxxxx时间20xx040520xx0417课程设计任务书题目学生选课管理系统系...

学生选课信息管理系统课程设计报告

数据库课程设计报告学生信息管理系统一系统开发平台11题目学生选课管理信息系统1要求系统可以准确地记录和查询学生信息包括学生的姓名单位年龄性别以及身份证号码等2要求系统可以准确地记录学生地每一次奖惩情况3系统可以...

学生信息管理系统课程设计报告

数据库课程设计报告指导老师贺亚茹组名组长组员完成日期成绩数据库课程设计报告一项目名称学生信息管理系统二项目需求分析1项目需求分析简介本数据库的用户主要是学生通过对用户需求的收集和分析获得用户对数据库的如下要求1...

学生选课系统课程设计报告(17篇)