Java小程序之客戶端的UI實現以及客戶端與服務器的UI交互(山寨QQ進行中......)


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. 循環第56個步驟


流程圖:

服務器消息處理流程圖:




客戶端登錄流程圖:


客戶端註冊流程圖:




用戶登錄聊天交互時序圖:




五、項目源代碼:

整個項目工程概覽圖:




服務器端:
模擬數據庫的具體實現: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、根據流程圖一步一步來,先寫好服務器端的代碼;
好了,就先分享到這吧!好睏了!
共勉!



發佈了80 篇原創文章 · 獲贊 192 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章