day04-1群聊功能

多用戶即時通訊系統04

4.編碼實現03

4.5功能實現-群聊功能實現

4.5.1思路分析

群聊的實現思路和私聊的實現非常類似。

不同的是:私聊時,服務端接收到消息後,只需要找出接收方的socket並發送消息即可

群聊時,服務端在接收到消息後需要遍歷集合中所有的執行緒,找出除了發送方的所有客戶端的socket,並發送消息

群聊思路:

  • 客戶端 – 發送者:
    • 用戶在控制台輸入資訊,客戶端接收內容
    • 將消息構建成Messgae對象,通過對應的socket發送給伺服器
  • 伺服器:
    • 讀取客戶端(發送者)發送給所有用戶(接收者)的消息
    • 從管理執行緒的集合中,遍歷所有執行緒,獲取所有socket(除了發送者本身)
    • 將Message對象轉發給所有的接收者
  • 客戶端 – 所有接收者:
    • 所有接受者分別在執行緒(通訊執行緒)中,讀取到發送者的message消息,並顯示即可

4.5.2程式碼實現

1.客戶端:
1.修改MessageType介面

在介面中增加新的消息類型

String MESSAGE_TO_ALL_MES = "7";//表示群發消息包
2.修改MessageClientService類

在該類中增加sendMessageToAll方法,實現群發功能

/**
 * 群發消息功能
 * @param content 內容
 * @param senderId 發送者
 */
public void sendMessageToAll(String content,String senderId){
    //構建 message
    Message message = new Message();
    message.setMesType(MessageType.MESSAGE_TO_ALL_MES);//設置消息類型是群發消息
    message.setSender(senderId);
    message.setContent(content);
    message.setSendTime(new Date().toString());//發送時間也封裝到 message對象中
    System.out.println(senderId + " 對大家說 " + content);

    //發送給服務端
    try {//在管理執行緒的集合中,通過userId來獲取執行緒,通過執行緒來獲取對應的socket,再通過socket獲取輸出流
        ObjectOutputStream oos =
                new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(senderId).getSocket().getOutputStream());
        oos.writeObject(message);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
3.修改ClientConnectServerThread類

在該類的run方法中增加新的邏輯業務,增加接收群發消息類型的判斷,並在控制台顯示

 else if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)) {
    //接收到的是群發的消息
    //就把伺服器轉發的消息,顯示到控制台即可
    System.out.println("\n" + message.getSendTime() + "\n" + message.getSender()
            + " 對大家說: " + "\n" + message.getContent());
} 

image-20220923163214511

4.修改QQView類

在該類的內層循環中,調用群發功能的方法:

case "2":
    System.out.println("請輸入想對大家說的話");
    String s = Utility.readString(100);
    //調用一個方法,將消息封裝成 message對象,發給服務端
    messageClientService.sendMessageToAll(s,userId);
    break;

image-20220923163420586

2.服務端:
1.修改MessageType介面

在介面中增加新的消息類型

String MESSAGE_TO_ALL_MES = "7";//表示群發消息包
2.修改ServerConnectClientThread類

在該類中增加新的業務邏輯

else if (message.getMesType().equals(MessageType.MESSAGE_TO_ALL_MES)) {
    //業務四:客戶請求群發消息需要遍歷管理執行緒的集合,把所有執行緒的socket都得到,然後將 message進行轉發即可
    //得到hm
    HashMap<String, ServerConnectClientThread> hm = ManageClientThreads.getHm();
    //遍歷
    Iterator<String> iterator = hm.keySet().iterator();
    while (iterator.hasNext()) {
        //取出所有userId
        String onlineUserId = iterator.next().toString();
        //取出除了發送者的所有用戶id
        if (!onlineUserId.equals(message.getSender())) {
            //轉發message
            //從集合中取出執行緒,在執行緒中取出socket,根據socket獲得輸出流,將socket的輸出流轉化為對象輸出流
            ObjectOutputStream oos =
                    new ObjectOutputStream(hm.get(onlineUserId).getSocket().getOutputStream());
            oos.writeObject(message);
        }
    }
} 

image-20220923163737014

3.修改ManageClientThreads類

在該類中增加方法,獲取集合

//返回hashmap
public static HashMap<String ,ServerConnectClientThread> getHm(){
    return hm;
}

運行程式:

1.運行服務端,進行監聽

image-20220923164142319

2.運行三個客戶端,登錄三個用戶

image-20220923164420287

3.在 用戶uid=100 的帳號發送群發消息

image-20220923164716432

4.其他用戶也接收到了消息

image-20220923165201230
image-20220923165230471

5.伺服器端

image-20220923165354965

功能實現完畢