續上文:
現在服務器已經的到了客戶端發來的信息了,但是存在一個問題。我現在服務器裡只有消息發送者這一個線程存在,接受者並沒有線程。所以需要在每個用戶登錄的時候就新建一個客戶端連接到服務器的線程。
回到Server類的驗證用戶登錄中,只要驗證成功,就新建一個這樣的線程。
if (u.getPassword().equals("123")) {
m.setMsType("1");
oos.writeObject(m);
// 單開一個與客戶端連接的線程
ServerToClientThread stct = new ServerToClientThread(s);
但是建立好的線程放到哪裡?才能在需要的時候就拿出來用呢?我們需要一個統一存放和管理ServerToClientThread線程的類,即一個HashMap。新建一個ManagerClientThread類。
publicclass ManagerClientThread {
為了方便以後直接訪問,並且該哈希表建立好後,就只有一張,不會增加和消失,於是用static修飾。Key值就用String類的登錄人ID表示,具體存放的則是ServerToClientThread線程。
publicstaticHashMaphm = new HashMap
();
新建一個add方法
// 向HM中添加客戶端通訊線程
publicstaticvoid addClientThread(String uid, ServerToClientThread ct) {
hm.put(uid, ct);
}
再新建一個getter方法
publicstatic ServerToClientThread getClientThread(String uid) {
return (ServerToClientThread) hm.get(uid);
}
至此,ManagerClientThread類的所有方法全部定義完成。我們就可以回到Server類中,在建立好一個ServerToClientThread後,把該線程存到HashMap中去。然後啟動該線程。
ManagerClientThread.addClientThread(u.getUserId(), stct);
stct.start();
至此,從客戶端到服務器的第一條線已經搭建完畢。下面需要從服務器識別消息的發送和接受者,再發給對應的客戶端。所以先回到ServerToClientThread類,繼續實現服務器的轉發。
//轉發
首先需要從ManagerClientThread中通過Message中的Getter屬性取得相應的線程。
ServerToClientThread sc = ManagerClientThread.getClientThread(m.getGetter());
再把該線程的Socket的IO流中的輸出流獲取到,把Message對象傳給這個流中。
ObjectOutputStream oos = new ObjectOutputStream(sc.s.getOutputStream());
oos.writeObject(m);
該oos流中的Message就是用戶A想要發送給用戶B的消息。現在假設我們登錄了用戶B,該如何得到該消息呢?首先當然是用一個ois.read方法讀內容並轉化為Message。但是假設用戶B同時接收到了用戶A和用戶C發來的信息,如果一個線程,那麼B在讀取了A發來的Message以後就不能再讀取C發來的Message了,所以這裡要使用多線程。新建一個ClientToServerThread類。
publicclass ClientToServerThread extends Thread {
private Socket s;
public Socket getS() {
return s;
}
publicvoid setS(Socket s) {
this.s = s;
}
public ClientToServerThread(Socket s) {
this.s = s;
}
重寫線程裡的run方法。
publicvoid run() {
while (true) {
ObjectInputStream ois;
try {
獲取s中的流即剛剛傳給服務器的oos流中的Message。
ois = new ObjectInputStream(s.getInputStream());
Message m = (Message) ois.readObject();
System.out.println("讀取到服務器發來的消息" + m.getSender() + "給"
+ m.getGetter() + "內容" + m.getCon());
// 把從服務器得到的消息顯示到該顯示的聊天界面
Chat chat = ManagerChat.getChat(m.getGetter() + " "
+ m.getSender());
chat.showMessage(m);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
由上可見,我們分辨的不是流中的特徵,而是新開啟一個可以分辨的Chat類的對象,這樣可以直接將Message分配給Chat。所以新建一個ManagerChat類來管理Chat對象。
publicclass ManagerChat {
privatestaticHashMaphm = new HashMap
(); //加入
publicstaticvoid addChat(String loginIdAndFriendId,Chat chat){
hm.put(loginIdAndFriendId, chat);
}
//取出
publicstatic Chat getChat(String loginIdAndFriendId){
return (Chat)hm.get(loginIdAndFriendId);
}
}
同樣使用HashMap的模式。Key值定義為登錄ID+聊天對象ID。所以在FriendList類裡,雙擊好友頭像後的方法裡要添加一句。
ManagerChat.addChat(this.ownerId+" "+friendNum, chat);
把該chat對象存到ManagerChat裡的HashMap中去。在ClientToServerThread的run 方法中就可以得到該chat,把讀取到的Message內容傳給chat中的showMessage方法。
在chat中寫一個showMessage方法:
publicvoid showMessage(Message m) {
String info = m.getSender() + "對" + m.getGetter() + "說" + m.getCon()
+ "\r\n";
}
而最開始我們定義專門連接的類Connect,就需要專門來啟動該線程,啟動的時間就是用戶登錄驗證成功以後,所以在驗證登錄Message的Type為1時,就啟動該線程。
// 創建一個該QQ和服務器連接的通訊線程
ClientToServerThread ccst = new ClientToServerThread(s);
// 啟動該通信線程
ccst.start();
ManagerClientToServerThread.addClientToServerThread(
((User) o).getUserId(), ccst);
我們做ClientToServerThread的目的就是讓一個用戶同時和多個用戶聊天,所以還需要一個ManagerClientToServerThread類來管理該線程。
o'di
這樣每一個用戶都有自己單獨的一個線程去與服務器取得聯繫,並且該線程可以讀取服務器發來的信息並提取出發送者和接受者,從HashMap中提出一個符合的chat對象,並讓chat對象顯示出消息。
java學習交流,資源分享可關注小編頭條號,點擊微頭條或者私信發 java獲取!歡迎私信小編。
【私信方法】文章上方處點擊“作者頭像”,進入作者首頁,在作者主頁上方點擊“ 關注” 旁邊的 “發私信” 即可。
最後的流程圖和類之間的關係:
【微信 、編程語言、java、互聯網、程序員】
閱讀更多 編程王者之路 的文章