Java小程序之客户端的UI实现以及客户端与服务器的UI交互(山寨QQ进行中......)
一、前言 通过前面的学习,我们已经能够自己创建客户端,不需要依赖系统自带的telnet客户端,但是,我们只能利用控制台和后台服务器进行交互了,今天,我们要实现客户端的UI化,以及UI化后的客户端与服务器进行交互;
二、客户端UI的实现思路 1、创建窗体,写一个简单的登录界面以及一个简单的注册页面; 2、界面写好后,我们需要进行逻辑处理,在什么时候创建客户端的连接;应该是当我们点击登录的时候,创建客户端的连接,连接创建好后,我们需要把用户输入的用户名和密码等信息发送给服务器;等服务器验证好后,等待服务器端的反馈信息;登录成功,则跳转到聊天界面;否则给出相应的提示信息; 3、注册处理:这里需要用到数据库来保存用户注册的信息;由于我们没有暂时没有学习数据库,只能用HashMap这一集合来模拟,但为了持久化的保存用户注册信息,我们需要利用文件输入输出流;每当有更新的时候,将更新后的数据写出文件,需要用到的时候,在将信息从文件中读出来(这里用到的第对象流的读写);这里,我们采用MVC模型进行业务分离;
三、我的完成步骤: 1、先写好数据库模拟功能,并测试该项功能功能是否正确; 2、分别写好客户端需要用到的三个界面,登录界面,注册界面,聊天界面 3、处理登录逻辑 4、处理注册逻辑 5、处理聊天逻辑 这个过程中,我们为了一次性将用户名和密码发送给服务器,我们采用的是userName#pass的形式,一次性将整条信息发送给服务器,服务器那边只需要把#作为分割符号,得到的字符串数组的第一个是用户名,第二个是密码,然后在进行验证就OK了;注册也有类似的原理;这里其实相当于我们自己定的一种协议;
四、流程分析以及流程图
流程分析:
a) 客户端登陆流程
i. 输入用户名密码
ii. 建立连接
iii. 发送操作类型(登陆操作:login,注册操作:regist)
iv. 发送用户名密码
v. 读取验证结果
vi. 根据结果判断:如果成功就进入聊天界面,如果失败就断开连接,继续登陆
vii. 前提登陆成功:读取消息和发送消息并行流程
1. 读取消息流程:从服务器读取一条完成的消息,并把消息追加到显示板上
2. 发送消息流程:输入消息内容到聊天板,把消息获取并发送给服务器
b) 客户端注册流程
i. 输入注册信息
ii. 建立连接
iii. 发送操作类型(登陆操作:login,注册操作:regist)
iv. 发送注册信息
v. 读取验证结果
vi. 断开连接
vii. 如果结果是成功的:跳转到登陆界面
如果结果是失败的:依然停留在注册界面
c) 服务端流程处理
i. 启动服务器
ii. 等待客户端连接
iii. 客户端连接成功
iv. 有两个分支:一个分支继续等待客户端连接,一个分支开始读取消息
v. 读取操作类型
vi. 操作类型如果是注册
1. 读取注册信息
2. 校验信息是否存在
3. 如果注册信息存在,反馈客户端注册失败消息(结束)
4. 如果注册信息不存在,录入注册信息
5. 发送注册结果(结束)
vii. 操作类型如果是登陆
1. 读取登陆信息
2. 校验登陆信息
3. 如果登陆失败,发送登陆失败结果(结束)
4. 如果登陆成功,发送登陆成功结果
5. 读取消息
6. 转发消息
7. 循环第5、6个步骤
流程图:
服务器消息处理流程图:
客户端登录流程图:
客户端注册流程图:
用户登录聊天交互时序图:
五、项目源代码:
整个项目工程概览图:
服务器端: 模拟数据库的具体实现:com.huaxin.database包:
用户信息类:
package com.huaxin.database;
import java.io.Serializable;
//实现Serializable接口,便于使用对象流
public class UserInfo implements Serializable{
private String userName;
private String password;
//构造函数
public UserInfo(String userName, String password) {
this.userName = userName;
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
数据库类:
package com.huaxin.database;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.JOptionPane;
public class Database {
public static HashMap<String,UserInfo> map = new HashMap<String,UserInfo>();
//调试代码
//static{
// //构造文件输出流
//FileOutputStream fos;
//try {
//fos = new FileOutputStream("database");
////构造对象输出流
//ObjectOutputStream oos = new ObjectOutputStream(fos);
//oos.writeObject(map);
//} catch (Exception e) {
//e.printStackTrace();
//}
//}
public static void main(String[] args) {
//调试代码
//Database.readDataBase();
//Database.add("user3", new UserInfo("user3","pwd3"));
//Database.readDataBase();
//Database.add("user1", new UserInfo("user1","pwd"));
//Database.add("user2", new UserInfo("user2","pwd"));
//Database.add("user3", new UserInfo("user3","pwd"));
//Iterator it =map.entrySet().iterator();
//while(it.hasNext()){
//Map.Entry<String,UserInfo> entry=(Map.Entry<String,UserInfo>)it.next();
//String key=(String)entry.getKey();
//UserInfo usin=(UserInfo)entry.getValue();
//System.out.println(usin.getUserName()+":"+usin.getPassword());
//}
}
static {
readDataBase();
UserInfo userSuper =new UserInfo("zhou","zhou");
map.put(userSuper.getUserName(),userSuper);
updateDataBase();
//System.out.println("已经添加初始数据!");
}
//数据库的增删查改函数
//增加用户
public static void add(String name,UserInfo userInfo) {
//保证读入都是最新的数据
readDataBase();
//System.out.println(name);
//System.out.println(map.get(name));
if(map.get(name)!=null){
JOptionPane.showMessageDialog(null, "该用户已存在!");
}else{
map.put(name, userInfo);
//调试代码
//Iterator it =map.entrySet().iterator();
//while(it.hasNext()){
//Map.Entry<String,UserInfo> entry=(Map.Entry<String,UserInfo>)it.next();
//String key=(String)entry.getKey();
//UserInfo usin=(UserInfo)entry.getValue();
//System.out.println(usin.getUserName()+":"+usin.getPassword());
//}
//System.out.println("已经添加!");
updateDataBase();
}
}
//删除用户
public static void delete(String name) {
//保证读入都是最新的数据
readDataBase();
if(map.get(name)!=null){
map.remove(name);
updateDataBase();
}else{
JOptionPane.showMessageDialog(null, "该用户不存在!");
}
}
//查找用户
public static UserInfo get(String name) {
readDataBase();
if(map.get(name)!=null){
return map.get(name);
}else{
JOptionPane.showMessageDialog(null, "该用户不存在!");
return null;
}
}
//修改用户
public static void update(String name,UserInfo userInfo) {
//保证读入都是最新的数据
readDataBase();
if(map.get(name)!=null){
map.put(name, userInfo);
updateDataBase();
}else{
JOptionPane.showMessageDialog(null, "该用户不存在!");
}
}
//利用文件将用户信息永久的保存起来
public static HashMap<String,UserInfo> readDataBase() {
try {
//构造文件输入流
FileInputStream fis = new FileInputStream("database");
//构造对象输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//将文数据库中信息读入进来
Object obj=ois.readObject();
HashMap<String,UserInfo> hm=(HashMap<String,UserInfo>)obj;
//将读入进来的数据赋给map
map=hm;
//Iterator it =hm.entrySet().iterator();
//while(it.hasNext()){
//Map.Entry<String,UserInfo> entry=(Map.Entry<String,UserInfo>)it.next();
//String key=(String)entry.getKey();
//UserInfo usin=(UserInfo)entry.getValue();
//System.out.println(usin.getUserName()+":"+usin.getPassword());
//}
//System.out.println("yijing duru wenjian 。。。。。。");
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
public static void updateDataBase() {
try {
//构造文件输出流
FileOutputStream fos = new FileOutputStream("database");
//构造对象输出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
//将当前数据写出到文件
//Iterator it =map.entrySet().iterator();
//
//while(it.hasNext()){
//Map.Entry<String,UserInfo> entry=(Map.Entry<String,UserInfo>)it.next();
//String key=(String)entry.getKey();
//UserInfo usin=(UserInfo)entry.getValue();
//System.out.println(usin.getUserName()+":"+usin.getPassword());
//}
oos.writeObject(map);
//System.out.println("已经更新数据库.....");
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
由于整个工程过于庞大,所以就不把所有的源代码全部贴出来了;想要学习交流的小伙伴们,请在下面找链接下载整个项目的源代码;重在思路的理解;
六、程序运行截图:
登录界面(自己做了优化) 注册界面
服务器端界面(前面已经见过了,服务器是自己看的,所以就没有继续优化了)
聊天界面和截图
两个客户端的互相聊天
七、项目总结 通过最开始服务器的创建,服务器的UI实现,客户端的创建,简单的多人聊天室等步骤,一步一步走过来了,现在其实到了最关键的时候了,第一次见到这么多张图,还是有点小怕的,但后来,其实只要对照流程图先把服务器端写好,然后,对照服务器的处理流程,把登录和注册写好就可以了;虽然期间遇到过无数的bug,但是一直没有放弃,反而在和bug斗智斗勇中,自己调试bug的能力也越来越好了;其实吧,说了这么多,最重要的就两点; 1、模拟数据库的搭建(这里我们就写了一天) 2、根据流程图一步一步来,先写好服务器端的代码; 好了,就先分享到这吧!好困了! 共勉!
项目源代码下载地址(仅限于学习交流,版权所有,请勿侵权)