多線程聊天室

項目名稱

  • 多線程聊天室

項目簡介

  • 多個用戶可以在同一局域網下進行聊天.

  • 類圖:
    在這裏插入圖片描述

  • 項目執行流程

客戶端通過對應端口號,和IP地址t與服務器端建立連接後,進行註冊,然後通過輸入輸出流,進行通信。
服務器端收到信息後,進行解析,判斷客戶端的需求(私聊,羣聊,下線等),從而進行相應的處理。
因爲採取多線程的方式,服務器端,線程比較多,採取線程池的方式進行處理。每個客戶端僅需要一個發送和一個接受線程,而且也不牽扯什麼資源共享的問題,直接繼承Thread類來實現多線程。
用一個ConcurrentHashMap存儲當前在線用戶信息。key–Socket,Value–String(用戶名)

項目基本功能

  • 註冊
  • 私聊
  • 羣聊
  • 下線
  • 顯示當前在線的用戶

項目擴展功能

  • 參數靈活化。將IP地址,端口號,線程池的線程數通過外界傳入。
  • 添加登錄功能。用戶註冊過之後,下線之後,下次直接登陸即可,無需重新註冊。
  • 收到歷史私聊信息。用戶下線之後,有人私聊對其發消息,當用戶再次上線後,可以查收到。
  • 客戶端異常關閉,服務器端進行相應的處理。

擴展方案

實現參數靈活化:

  • 通過鍵盤將參數運行時讀入。
  • 通過main方法的args參數傳入。
  • 通過文件讀入。
  • 通過數據庫傳入。

最終選擇main方法的參數進行傳入,因爲需要的參數(端口號,IP地址,線程數目)比較少,且很方便,便於施行。

實現登錄功能

  • 在HandelClient類中,添加一個靜態內部類User,存放用戶的用戶名和密碼及與對象相關聯的一個消息文件(文件後期實現上線後收到私聊消息有用)。
  • 在HandelClient類中,添加一個靜態常量USER_SET,本身是一個CopyOnWriteArraySet(線程安全的Set集合)。
  • 用戶進行註冊時,就用用戶的信息生成User對象,存進Set中。之所以選擇Set是因爲Set裏面的元素不可重複,且查詢時間複雜度O(1).
  • 用戶在選擇登陸功能時,只需判斷Set中是否包含該對象即可,存在就將該用戶放進ConcurrentHashMap中就OK了。

實現收到歷史私聊信息。

業務需求說明,用戶下線之後,有人對其私聊,將該信息存儲起來,待用戶再次上線後,將消息發過去。

  • 在進行私聊時,判斷用戶想要聊天的目標用戶,如果該用戶在ConcurrentHashMap中,證明該用戶還在線,否則判斷是不是在CopyOnWriteArraySet中,在就說明該目標用戶是一個合法的註冊過的.
  • 因此將相應的消息存儲在目標用戶的文件中,當該目標用戶上線之後,將所有消息發送過去,並且將文件內容清空(很重要)。

客戶端異常關閉,服務器端進行相應的處理。

待續…

遇到的問題及解決方案

  • 問題一:客戶端收不到消息(1.服務器沒發過來? 2.客戶端讀不來?)
    1.沒有使用println。
    2.使用了仍然沒有發出去,沒有flush。
  • 問題二:一次註冊,終生使用
    程序結束時,將用戶名和密碼按行寫進文件
    程序運行時,從文件裏面按行都出來,拆分生成新對象,放進Set集合中。
  • 問題三:流採用自動關閉的方式
    流關閉的太快了,導致Socket中斷。最終只能採取手動關閉。
  • 問題四:IP地址的驗證
    相對來說比較簡單,字符串拆分加判斷就OK了。
  • 文件內容的問題
    用戶上線之後,將私聊消息發過去之後,需要將文件內容清空,否則下次依然發原始的信息。

項目源碼

https://github.com/excellent01/Java-Code

項目難點

  • 實現登陸功能,
  • 下線後再次上線可以收到私聊消息。

知識體系

  • Java集合框架
  • JUC包下的線程安全的集合
  • 多線程
  • Java網絡編程
  • JavaIO
  • 其它知識

效果展示

服務器端啓動

在這裏插入圖片描述

客戶端啓動並註冊

在這裏插入圖片描述

羣聊功能

在這裏插入圖片描述

登錄功能及上線後收到私聊信息

在這裏插入圖片描述

不足之處

因爲暫時沒有學習數據庫,不能將用戶最終的信息,存入數據庫,只能使用文件代替.

小段代碼:IP地址的校驗

private static boolean hostIsLegal(String host) {
   if(host == null)
    return true;
   String[] arr = host.split("\\.");
   if(arr.length != 4){
       return false;
   }
   for(String str : arr){
       try{
           int num = Integer.parseInt(str);
           if(num < 0 || num > 255){
               return false;
           }
       }catch (NumberFormatException e){
           return false;
       }
   }
   if(host.endsWith("\\.")){
       return false;
   }
   return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章