(2009届)
本科生毕业设计(论文)
基于Java的网络通信系统设计与实现
学 院、系: | 计算机与通信学院 | |
---|---|---|
专 业: | 通信工程 | |
学 生 姓 名: | ||
班 级: | 学号 | |
指导教师姓名: | 职称 | |
最终评定成绩 |
2009年6 月
在网络越来越发达的今天,人们对网络的依赖越来越多,越来越离不开网络,由此而产生的聊天工具越来越多,例如,国外的ICQ、国内腾讯公司开发的OICQ。随着网络聊天一类的聊天系统的发展日新月异,因此产生了制作一个类似QQ的网络聊天工具。Java是一种程序设计语言,它具有简单的、完全面向对象以及与平台无关的结构,也具有可移植性、高性能和安全性,并提供了多线程的功能,而Java语言最大的成功之处在于它的平台无关性和具有强大的网络编程功能,基于Java网络编程的强大功能,本人将用Java编写一个网络聊天系统。
论文首先论述了系统的开发背景,并对所用到的开发工具与关键技术做了简单的介绍。接着对系统的研究意义,研究现状及设计目标进行分析,通过对系统需求和可行性进行分析,确定了系统的功能模块,并画出相应的功能结构图、模块图和数据流图。其次按系统总体设计的结果,对系统中的数据库进行结构设计。
一般来说,聊天工具大多数由客户端程序和服务器程序,外加服务器端用于存放客户数据的数据库组成,本系统采用客户机/服务器架构模式,通过Java提供的Socket类来连接客户机和服务器并使客户机和服务器之间相互通信,由于聊天是多点对多点的,而Java提供的多线程功能,用多线程可完成多点对多点的聊天。数据库管理系统用SQL Server2000,完成并通过JDBC-ODBC桥访问数据库。聊天系统完成后将可进行多人对多人的聊天,对好友进行添加、删除,对新用户的注册,发送消息、接受消息,传输文件等功能。界面设计细分化,方便使用者操作和理解。服务器实现了查询和修改等功能,程序逻辑联系较紧密。
关键词:JAVA,C/S,SQL Server 2000, 多线程, 数据库管理系统
More and more developed in the network today, people rely on more and more networks, and can not leave with out it. This caused the chat materials become more numerous, as the overseas ICQ system, the OICQ system that invented by Tencent Co., and so on. So we create a network chat medium just like the QQ.Java is a programming language with the simple construction which is completely object-oriented 。Java also possess the portability, high performance and security, and provide the multi – thread function. The best success is its and its strong network programming function, base on this, I will use Java to write a network chat system.
Firstly, the paper discussed the development background of the system, and made a briefly introduction to the development tool and key technologies. Then analyzed the system’s research significance , research present situation and project objective .According to the requirement analysis and feasibility analysis ,it can confirm the system’s functional module and draw corresponding functional configuration chart ,module chart and data stream chart. On the basis of the result of system designing, the paper design database by structured design method.
Generally speaking, most chat medium consist the client program, server program and the server which is use to content the database. This system adopt client/server pattern and the Socket class provide by Java to communicate the client and server. Because of chat is many to many, the multithreading function can implement the many-many chat, database management system use SQL Server2000 and JDBC-ODBC bridge to visit the database.
When chat system is achieved, it can possess functions like chat in a many-many way, add and delete the intimate, the new user’s registration, and sending and receiving message, transferring files and so on. The refinement of interface designing would facilitate user operation and understanding. The contact surface has the thin differentiation designs; modules have realized the functions such as inquiry, revision and so on. The logic relationship in this program is closed.
Keyword: Java, Multithreading, Client/Server, SQL Server2000,
Database Management System
目 录
第1章 绪论1
1.1 课题选择的背景1
1.2 课题选择意义1
1.3 系统开发技术简介2
1.3.1 Java简介2
1.3.2 JDBC技术简介2
1.3.3 数据库Microsoft SQL-Server 2000 简介4
1.3.4 多线程介绍4
第2章 系统分析6
2.1 系统需求6
2.1.1 系统的特点分析6
2.1.2 网络聊天系统需要解决的问题6
2.2 系统设计思想6
2.3 系统实现功能分析7
2.4 系统开发及运行环境7
第3章 系统总体设计8
3.1 总体构架8
3.2 系统整体功能8
3.3 系统模块功能设计9
第4章 数据库设计12
4.1 系统E-R图12
4.2 数据字典12
4.3 各个数据表的创建12
4.4 数据库的连接13
第5章 系统实现与编码15
5.1 服务器功能模块的设计15
5.1.1 Socket和ServerSocket介绍15
5.2 服务器功能模块的实现16
5.2.1 多线程服务器模块实现16
5.2.2 在线用户管理模块实现18
5.2.3 部门管理模块实现20
5.2.4 用户管理模块实现21
5.2.5日志管理模块实现22
5.3协议设计与实现23
5.3.1 协议规则23
5.3.2 协议实现24
5.4客户端功能模块的设计与实现27
5.4.1 登陆认证模块27
5.4.2 主界面模块27
5.4.3 聊天模块28
5.4.4 文件传输模块29
第6章 系统测试33
6.1 系统测试概述33
6.1.1 测试目标33
6.1.2 测试实例研究与选择33
6.1.3 系统测试环境33
6.2 测试内容34
6.2.1 服务器模块测试34
6.2.2 客户端模块测试34
6.2.3 平台兼容性测试35
6.3 测试结果分析评价35
结 论36
参考文献37
致 谢38
信息时代的到来引发了一场知识和信息革命,计算机和现代网络技术的广泛应用极大地促进了社会经济的发展,同时也带来了一些新兴产业的发展,比如聊天软件。随着计算机网络日新月异的发展,人们的交流方式越来越多,传统的交流方式已经难以满足人们的交流的需求了,网络聊天已经慢慢成为人们生活的一部分,呈现出良好的势头和广阔的发展前景。在互联网上即时的和好友取得联系,已经成为当今社会人们主流的联系方式。同时,即时通信系统对现代企业也有着重大意义,它能实现快速人际交流、数据共享,从而提高效率和生产力。实时通信为诸多企业开拓了网络应用的新领域。自从它诞生以来,以实时交互、资费低廉等优点,受到广大个人用户的喜爱,已经成为网络生活中不可或缺的一部分。越来越多的企业已开始认识到即时通信工具能够带来极高的生产力,并借助它的应用,来提高业务协同性及反馈的敏感度和快捷度。网上聊天系统是为人们之间进行交流和联系提供的一个平台。通过提供完善的网上聊天系统服务,可以达到增进彼此之间的了解,增进人与人之间的感情交流。因此,两台计算机之间进行即时通讯、发送文件等交流方式已经成为必然潮流。于是出现了QQ、UC 等聊天工具,然而QQ、UC 等聊天工具虽然方便、实用,但是,娱乐功能太多,有很多吸引人的娱乐功能,从一定程度上来说,是一种娱乐工具,不能作为用于即时通讯的专用工具。目前,用于实现单一的即时通讯的软件实在太少,为此,我们决定开发一个专用于实现多台计算机之间即时通讯的软件,以方便多台计算机之间信息的快速交流。
计算机越来越成为我们生活中的重要工具,自从1968年世界上第一个计算机网络--ARPA网(美国国防部高级研究计划网)投入运行以来,计算机网络技术在全世界范围内迅速发展,犹如雨后春笋,各种网络纷纷涌现。不同国家的计算机网络相互连接,形成跨国计算机网络,促进了世界各国之间的科技、文化和经济交流。在电子商务的应用中,计算机网络作为基础设施,将分散在各地的计算机系统连接起来,使得计算机之间的通讯在商务活动中发挥了重要的作用,从此计算机的作用不再那么单调,现在Internet把几乎全球的服务器相互连接起来,这样不仅使网络上的计算机之间进行相互访问并下载对自己有用的资源,还可以用来写信、听音乐、看电视、通过可视电话进行通信,通过Internet网进行信息查询、网上购物等。计算机正在改变我们的通信、工作和娱乐的方式。
Java是由Sun Microsystems公司于1995年5月推出的Java程序设计语言(以下简称Java语言)和Java平台的总称。它以C++为基础,但是却是一个全新的软件开发语言。Java是一个简单,面象对象,分布式,解释性,强壮,安全,与系统无关,可移植,高性能,多线程和动态的语言-------这是 Sun给Java的定义。
Java平台由Java虚拟机(Java Virtual Machine)和Java 应用编程接口(Application Programming Interface、简称API)构成。Java 应用编程接口为Java应用提供了一个独立于操作系统的标准接口,可分为基本部分和扩展部分。在硬件或操作系统平台上安装一个Java平台之后,Java应用程序就可运行。现在Java平台已经嵌入了几乎所有的操作系统。
Java是一种简单的,面向对象的,分布式的,解释型的,健壮安全的,结构中立的,可移植的,性能优异、多线程的动态语言。Java 对开发者具有不可估量的价值,使他们可以:
本次设计主要实现了以下几个功能:
● 客户端功能设计如下:
● 服务器端功能设计如下:
基于Java的网络聊天系统设计与实现主要考虑三个个方面的设计,即服务器模块设计,服务器客与客户端通信规则设计和客户端模块设计,本系统整体构架如图3.1所示。
图3.1 系统整体构架图
基于JAVA的网络聊天系统分为服务器端和客服端,其中:
服务器端包括:在线用户管理模块,用户管理模块,部门管理模块,日志管理模块;
客服端包括:用户认证模块,主界面模块,聊天模块,文件传输模块。
系统整体功能设计如下图2.2所示
图3.2 系统整体功能设计图
整个系统分为3个模块,其中主要功能如下:
(一)服务器端模块
服务器端模块首先设计多线程来处理客户端的连接,当受到客户端请求的时候,建立一个新的线程来处理客户端的连接。并且在一个注册中心中登记该线程,并存储客户端用户的一些信息,方便服务器统计在线用户,以便与这些在线用户进行通信。除此之外,为了方便用户更好的操作和掌握服务器端,系统还设计四个管理服务器的模块,分别是在线用户管理模块,用户管理模块块,部门管理管理模块和日志管理模块。各个模块功能设计如下:
日志管理模块主要实现服务器运行状态信息,以及注册用户登陆的一些信息进行记录,管理员可以在需要的时候查看日志,监控服务器的一些状态,以及客户端一些用户的状态。
(二)协议规则模块
该模块主要实现服务器与客户端之间通信规则的制定。其具体思路是:在服务器使用Socket通信的时候,把服务器与客户端通信的信息封装为一个类对象,然后通过自己手动将这些类对象转化为输入流,并在另一端输出流,根据自己制定的规则逆序解析流,把流转化为类对象。实现服务器与客户端之间的通信。其中具体的表示信息要自己制定,比如登陆成功标示,失败标示,修改密码标示,聊天标示等,这些都需要自己通过一些数字标记。
(三)客户端模块
客户端模块主要实现,用户账号到服务器的认证,以及登陆之后与其他用户通信或者部门通信,除了简单文本聊天之外,也考虑用户之间文件传输的功能。其中客户端之间的通信,是在客户端之间搭建服务器,也即客户端自己建立SocketServer,并用多线程来处理对不同用户的聊天。用户之间文件传输也是通过搭建客户端之间的服务器来实现文件出传的,不需要通过服务器来实现信息的传输,可以减少延迟,也减少了服务器端的流量损失。但是在客户之间搭建连接的时候仍然需要通过服务器来实现一些简单的通知操作,实现客户端与客户端之间搭建其连接。因此客户端大体分四个模块,分别是用户认证模块,主界面模块,聊天模块和文件传输模块。各个功能模块设计如下:
基于网络聊天系统数据库E-R模型,如图 所示:
图4.1 聊天系统E-R图
基于网络聊天系统数据库包括部门信息以及用户信息,部门信息表格和用户信息表格的设计结果如表4.1,表4.2所示:每个表格表示在数据库中的一个表。
表4.1部门信息表(T_DEPARTMENT)
列名 | 数据类型 | 可否为空 | 字段名称 | 字段大小 |
---|---|---|---|---|
D_ID | VARCHAR | No null | 分组ID | 3 |
D_NAME | VARCHAR | √ | 分组名字 | 50 |
D_REMARK | VARCHAR | √ | 标记 | 100 |
表4.2 用户表(T_USER)
列名 | 数据类型 | 可否为空 | 字段名称 | 字段大小 |
---|---|---|---|---|
U_ID | VARCHAR | Not null | 用户ID | 6 |
U_NAME | VARCHAR | √ | 用户名 | 50 |
U_PASSWORD | VARCHAR | √ | 用户密码 | 20 |
U_SEX | VARCHAR | √ | 性别 | 3 |
U_ICONID | INT | √ | 头像ID | 4 |
U_DEPTID | VARCHAR | √ | 部门ID | 3 |
U_AGE | INT | √ | 年龄 | 4 |
U_TEL | VARCHAR | √ | 电话号码 | 20 |
U_ADDRESS | VARCHAR | √ | 地址 | 100 |
U_REGTIME | DATETIME | √ | 注册时间 | 8 |
U_ISONLINE | INT | √ | 状态 | 4 |
打开控制面板,找到管理工具,在选中数据源配置,进行ODBC数据源的配置其中主要的配置流程如下面图所示:
图4.2 配置QQ数据源界面
图4.3 配置QQ数据库界面
图4.4 测试数据源连接
Socket,简称套接字,用于实现网络上客户和服务器之间的连接。也就是说网络上两个或两个以上双工方式通信的进程之间总有一个连接,这个连接的端点成为套接字,套接字是在比较低的层次上通信的。
具体的说:一个服务器应用程序一般侦听一个特定的端口等待客户端的连接请求,当一个连接请求到达时,客户端和服武器端建立一个通信连接,在连接过程中,客户端被分配一个本地端口与一个socket建立连接,客户端通过写socket来通知服务器,以读socket中的信息,类似的服务器也获得一个本地端口,它需要一个新的端口号来侦听原始端口上的其他连接请求。服务器也通过它的本地端口连接一个socket,通过读写和客户端通信。
Socket程序的工作过程:
1、建立Socket连接:在通信开始之前由通信双方确认身份,建立一条专用的虚拟连接通道。
2、数据通信:利用虚拟连接通道传送数据信息进行通道。
3、关闭:通信结束时,再将所建的虚拟连接拆除。
实现套接字的服务端,需要使用ServerSocket 类。ServerSocket类是服务器程序的运行基础,它允许程序绑定一个端口号来监听客户端的请求,一旦产生客户端请求,它将接受这一请求,同时产生一个完整的Socket 连接对象。服务器绑定的端口必须公开,以便让客户端程序知道如何连接这个服务器。同时,作为服务器,它必须能够接收多个客户的请求,这就需要为服务器设置一个请求队列,如果服务器不能马上响应客户端的请求,要将这个请求放进请求队列中,等服务器将当前的请求处理完,会自动到请求队列中按照先后顺序取出请求进行处理。服务器的资源是有限的,这就导致它的最大连接数是有限的,通过ServerSocket 的构造函数可以指定这个最大连接数。如果不明
确指定这个连接数,默认最大连接数为50,也就是说,客户端的请求队列最大可以容纳50 个请求,当超过这个最大连接数时,用户的请求将不再会被响应。 利用SocketServer 也提供了一些方法,它们主要有:
accept() 返回一个“已连接”的Socket 对象
getInetAddress() 得到该服务器的IP 地址
getLocalPort() 得到服务器所侦听的端口号
setSoTimeout() 设置服务器超时时间
getSoTimeout() 得到服务器超时时间
服务器和客户端通过Socket简单通信框架下图所示。
图 5.1 服务器和客户端通过Socket简单通信框架
服务器模块核心功能是使用Socket Server实现多线程的服务器,针对每一个客户建立一个单独的线程处理客户端的请求。其具体思路是,首先建立Socket Server,并绑定服务器IP和某个未使用的端口。然后监听该端口,如果有客户端的Socket连接则建立一个客户端线程ClientThread类对象,有该对象处理该客户的一些请求,并在ClientThread中保存User Bean对象,该对象保存了用户的一些基本信息,比如账号、密码、年龄、是否在线等。如果服务器没有关闭Socket Server则服务器则一直处理客户端连接。其核心代码如下。
public class MessageServer extends Thread {
//服务器端SocketServer
private ServerSocket server;
private boolean isStop = false;
public MessageServer(int port) throws IOException {
server = new ServerSocket(port);
}
/**
* 消息服务
*/
public void run() {
Socket client;
try {
while (!isStop) {
//建立处理客户端连接的线程
client = server.accept();
(new ClientThread(client)).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e1) {
// e1.printStackTrace();
}
}
isStop = true;
}
}
}
该模块主要实现在线用户统计、多线程服务器的启动关闭、以及服务器发送系统公告等功能。其中在线用户统计是在客户端每次连接的时候服务器建立了ClientThread来单独处理某个客户请求,如果该客户登陆成功的话则在工具类PubValue中一个HashMap添加该用户信息,查看在线用户信息的时候,可以通过工具类PubValue来的HashMap读取在线用户列表。系统公告则是通过服务器根据在线用户列表,广播系统公告到每个客户端,实现信息的公布。其实核心代码如下:
private void login(Message message) throws IOException {
if (message.getType().equals(PackOper.LOGIN)) {
User user = (User) message;
List list = UserDaoFactory.getUserDao().selectId(user.getId());
if (list.size() > 0) {
User tempUser = (User) list.get(0);
if (tempUser.getPassword().equals(user.getPassword())) {
if (tempUser.getIsOnline() == Parameter.ONLINE) { message.setType(PackOper.LOGIN_ONLINED);
myObjectOut.writeMessage(message);
this.setStop();
return;
}
ServerUI.getInstance().getLogUI().addLog(
LogOper.getInstance().insertOnLineLog(tempUser));
//登录成功后
//先通知所有人
//再通知此用户
//再把自己加到线程组中
//接着更新数据库
//下载树给此用户
tempUser.setType(PackOper.LOGIN_SUCCEED);
tempUser.setIsOnline(pub.Parameter.ONLINE);
this.chatCompany(tempUser);
this.user = tempUser;
this.myObjectOut.writeMessage(tempUser);
PubValue.addUserThread(this.user.getId(), this);
UserDaoFactory.getUserDao().setOnline(tempUser.getId(),
Parameter.ONLINE);
ServerUI.getInstance().getOnLineUI().updateOnLine();
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
// e1.printStackTrace();
}
// 登录成功后发送树
PubValue.company.setType(PackOper.ADD_COMPANY);
myObjectOut.writeMessage(PubValue.company);
list = DepartmentDaoFactory.
getDepartmentDao().select(null);
Iterator it = list.iterator();
while (it.hasNext()) {
message = (Message) it.next();
message.setType(PackOper.ADD_DEPARTMENT);
myObjectOut.writeMessage(message);
}
list = UserDaoFactory.getUserDao().select(null);
it = list.iterator();
while (it.hasNext()) {
message = (Message) it.next();
message.setType(PackOper.ADD_USER);
myObjectOut.writeMessage(message);
}
return;
}
}
message.setType(PackOper.LOGIN_DEFEATED);
myObjectOut.writeMessage(message);
this.setStop();
}
}
其实现效果如图所示:
图5.2 聊天服务器端界面
该模块主要实现对部门进行管理,根据情况添加,删除或者修改部门等。其设计主要是通过SQL语句来操作数据库数据的。跟普通的管理系统设计思路一样。没有太多的算法,主要是SQL语句的构造,以及数据库操作的API函数的使用。其实现效果如图所示.
图5.3 部门管理模块界面
该模块类似于部门管理模块的设计,主要是针对用户的一些信息进行管理,根据需要添加、删除或者修改用户信息。其实现主要也是SQL语句的构造以及JDBC驱动提供函数的使用。比较复杂的部分设计主要体现界面的设计,如何使用Swing构造友好并且易于交互的界面需要花费一些时间。以及实现组合条件的用户查询,可以根据用户账号查询,或者所在部门查询,或者编号查询,或者根据这三个条件组合查询,这一功能比较复杂,如何根据不同的情况构造合适的SQL语句进行数据查询,显得很重要,否则粗略的设计会增加很多冗余代码。其整体实现效果如下图5.4所示。
图5.4 用户管理界面
日志管理模块主要是监控服务器的一些状态,以及客户端用户登陆的情况。这些日志信息记录是通过工具类LogOper实现日志的记录。该类使用了设计模式中单例模式,保证服务器端在进行日志操作的时候只有一个实例在内存中进行日志的记录。其日志的存放路径主要是在系统目录下,其文件名为server.log。通过服务器UI界面查询日志信息也是通过读取该文件来查询日志记录的。其实现效果如下图5.5:
图5.5 日志管理模块界面
协议规则里主要用一些整数来表示服务器与客户端通信的时候包的类型。服务器可以按照这个规则来解析包,并根据包中的信息做出相应的操作。其标示的具体含义和定义规则下表5.1所示。
表5.1 协议规则表
标示名字 | 标示值 | 标示意义 |
---|---|---|
LOGIN | 10 | 登录包标识 |
LOGIN_SUCCEED | 11 | 登录成功标识 |
LOGIN_DEFEATED | 12 | 登录失败标识 |
LOGIN_ONLINED | 13 | 已经在线标识 |
ADD_COMPANY | 14 | 添加公司标识 |
ADD_DEPARTMENT | 15 | 添加部门标识 |
ADD_USER | 16 | 添加用户包标识 |
UPDATE_COMPANY | 17 | 更新公司信息标识 |
UPDATE_DEPARTMENT | 18 | 更新部门信息 |
UPDATE_USER | 19 | 更新用户信息 |
DELETE_DEPARTMENT | 20 | 删除部门 |
DELETE_USER | 21 | 删除用户 |
CHAT_USER | 22 | 私聊 |
CHAT_DEPARTMENT | 23 | 部门聊天 |
CHAT_ALL | 24 | 公司聊天 |
MESSAGE | 25 | 公司通知 |
UPFILE | 26 | 要求传送文件 |
UPFILE_FIAT | 27 | 许可传送文件 |
SEND_DEFUSE | 28 | 发送方取消 |
UPFILE_DEFUSE | 29 | 不许可传送文件 |
DOWN_LINE | 30 | 下线包 |
FORCEDOWN_LINE | 31 | 强制下线包 |
UPPASSWORD | 32 | 修改密码 |
UPPASSWORD_DEFEATED | 33 | 原密码不对 |
UPPASSWORD_NEW_NULL | 34 | 新密码为空 |
UPPASSWORD_SUCCEED | 35 | 修改密码成功 |
SERVERCLOSE | 36 | 服务器关闭 |
协议的指定主要是考虑在进行通信的过程中,保证服务器和客户端的通信消息能够被双方正确的理解,并作出相应的处理,比如登陆失败消息,登陆成功消息,文件传输消息,聊天消息等,服务器和客户端必须要按照指定的规则去打包和解析消息。其中协议的设计是通过对Socket流进行自定义的封装,把已经定义好的对象的每一个属性按照unicode编码中的一些特殊的编码\u0000(空格)进行分隔封装,并发送输入和输出流,另一端安装封装的逆序把输出流封装为对象,并识别该对象随对应是什么样的消息,客户端或者服务器作出相应的处理。而通过继承输入输出流实现了服务器器信息的发送,但是具体的包类型是什么样的消息是通过标示来标记。通过标示来解析这些包是什么样类型的包,实现服务器和客户端之间可理解的通信。其核心类PackOper类主要代码如下:
/**
* 解包类
* @author Administrator
*/
public class PackOper implements PackInterface {/**
* 创建一个包,根据object类型
* @param object
* @return
*/
public byte[] createPackage(Object object) {
if (object == null) {
return null;
}
if (object instanceof MessagePack) {
return createMessagePack((MessagePack) object);
}
if (object instanceof User) {
return createUser((User) object);
}
if (object instanceof Department) {
return createDepartment((Department) object);
}
if (object instanceof Company) {
return createCompany((Company) object);
}
return null;
}
/**
* 创建消息包
* @param pack
* @return
*/
private byte[] createMessagePack(MessagePack pack) {
return createByte(STARTSEPARATOR + pack.getType() + SEAS
+ pack.getFrom() + SEAS + pack.getFromIP() + SEAS
+ pack.getFromPort() + SEAS + pack.getTo() + SEAS
+ pack.getMessage() + ENDSEPARATOR);
}
/**
* 解一般消息包
* @param strs
* @return
*/
private Message unbindMessagePack(String[] strs) {
MessagePack messagePack = null;
if (strs != null) {
try {
messagePack = new MessagePack();
messagePack.setType(strs[0]);
messagePack.setFrom(strs[1]);
messagePack.setFromIP(strs[2]);
messagePack.setFromPort(Integer.parseInt(strs[3]));
messagePack.setTo(strs[4]);
messagePack.setMessage(strs[5]);
} catch (Exception e) {
return null;
}
}
return
}
}
MyObjectInputStream.java,该文件继承输出流,并自定义解析输出流的规则。Unit包里封装了所有消息类封装Bean。MyObjectInputStream.java,该文件继承输入流,并自定义解析输入流的规则。PackOper.java该文件主要实现了,Socket发送字节流的解包规则,并转化字节流为对象。也即服务器实现客户端不同消息识别,服务器端并作出相应处理。
该模块主要实现用户的登陆认证,以及服务器代理IP和端口的设置,并检查数据的合法性。主要是参考现在通用即时聊天工具QQ界面进行设计,没有采用具体的算法实现漂亮的算法,只是截取QQ界面的图片作为自己设计界面的背景。同时用户登录身份认证使用简单模拟的QQ协议通过发送登陆验证包消息给服务器,服务器解包之后查询数据库检查用户身份的合法性,并发送给客户端认证成功或者失败包,客户端解包之后如果认证成功则登陆成功,显示主界面,否则提示用户登陆失败,提示用户重新登陆。并且可以根据服务器IP和端口的变化进行动态设置服务器地址,具有很好的灵活性。具体实现效果如下图5.6所示。
图5.6 用户登陆界面
该模块是参考QQ设计,截取QQ图片作为自己界面设计背景素材。其中分组信息也是通过向服务器发送查询该客户端用户的分组以及好友信息包,服务器解包之后查询数据库,并将查询结果封装成对象之后,转化为Socket流发送给客户端,客户端将包信息解析封装为对象,并显示数据库到客户端主界面上。该模块设计难点主要在如何有效的使用Swing提供的现有组建去扩展,实现漂亮的交互界面。如图5.7:
图5.7 用户登陆后主界面
聊天模块设计思路如下:
该模块是现在比较流行的QQ作为参考,设计的聊天的界面,特别是表情的发送,表情的窗口是FaceUI类封装的,它调用相应的辅助类来实现模拟QQ表情的实现,同时,自定义封装了聊天文本框,来实现文字和图片混合的文本框,在发送的时候获取用户发送的内容,自己封装为预定义好的对象,并转化为Socket流发送,经过服务器,有服务器找到对应客户端并转发消息,另一个客户端运行专门的客户端线程检测消息,接收到服务器的消息之后,自动弹出聊天窗体,并把好友发过来的消息显示在聊天界面上。其中,ChatUI类封装了聊天主界面,实现聊天的功能。FaceUI类实现聊天的时候可以添加表情。其实现效果如下图5.8:
图5.8聊天界面
该模块主要实现了用户之间的聊天功能,可以传输表情和文字。而且也可以实现不同用户之间多线程的文件传输功能,可以在聊天的同时不影响文件传输并行进行,而且多线程传输文件也提高了文件的传输速度。实现技术组要采用多线程文件传输模块设计,主要实现不同用户之间进行发送文件,SendStreamThread类,该类主要采用多线程的思想,发送Socket流,实现客户端和服务器的通信,并通过服务器实现客户端与另一个客户端通信的功能。ReceiveStreamThread类,该类主要采用多线程的思想,接收Socket流,实现客户端和服务器的通信,接收Socket流并根据QQ协议规则将Socket流转化为相应的消息对象,并通知本地主线程,作出相应的处理。该类无论是在服务器或者是客户端都在一直运行,只要发现有Socket流到来就接收,并交给主线程进行处理。其核心代码如下,以ReceiveStreamThread类为例。
public class ReceiveStreamThread extends Thread {
private Socket socket;
private TransTask transTask;
public ReceiveStreamThread(Socket socket) {
this.socket = socket;
}
public void run() {
BufferedInputStream bufInStream = null;
BufferedOutputStream bufOutStream = null;
MyObjectInputStream myObjectIn = null;
MyObjectOutputStream myObjectOut = null;
try {
myObjectIn = new MyObjectInputStream(socket.getInputStream());
myObjectOut = new MyObjectOutputStream(socket.getOutputStream());
MessagePack msgPack = (MessagePack) myObjectIn.readMessage();
if (!msgPack.getType().equals(PackOper.UPFILE)) {
socket.close();
return;
}
// 寻找是否有此任务
this.transTask = TransFileStock.getReceiveFileUI(msgPack.getMessage());
if (transTask == null) {
socket.close();
return;
}
msgPack.setType(PackOper.UPFILE_FIAT);
myObjectOut.writeMessage(msgPack);
bufInStream = new BufferedInputStream(socket.getInputStream());
bufOutStream = new BufferedOutputStream(new FileOutputStream(
transTask.getSendFile()));
byte[] bufByte = new byte[1024];
int readLen = -1;
int readSum = 0;
while ((readLen = bufInStream.read(bufByte, 0, bufByte.length)) > 0
&& !transTask.getFileUI().isCancel()) {
bufOutStream.write(bufByte, 0, readLen);
readSum += readLen;
transTask.getFileUI().setPlan(readSum);
}
bufOutStream.flush();
if(readSum < transTask.getFileLength()){
transTask.getFileUI().setClose();
}else{
//传送完毕
transTask.getFileUI().setTransFinish();
}
TransFileStock.removeReceiveTrans(transTask.toString());
} catch (UnsupportedEncodingException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成 catch 块
e.printStackTrace();
} }
}
其实现效果如下图5.9所示。