首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Socket 實戰:從廣播推送到單聊實現完整演進

     編輯:Java 相關知識

上篇實現了伺服器向所有客戶端廣播推送訊息,本文進階改造為一對一私聊模式:伺服器維護用戶 ID 與 Socket 映射,根據訊息中的 friendId 精準路由轉發,同時保留廣播功能。完整展示實體類設計、伺服器路由邏輯、客戶端登入狀態區分與雙向通訊流程。

核心實體類設計

// 用戶資訊
public class UserInfoBean implements Serializable {
    private long userId;
    private String userName, likeName, userPwd, userIcon;
    // getter/setter
}

// 聊天訊息
public class MessageBean implements Serializable {
    private long messageId, groupId;
    private boolean isGroup;
    private int chatType; // 1文字 2圖片 3視頻 4文件 5位置 6語音 7視頻通話
    private String content, errorMsg;
    private int errorCode, userId, friendId;
    private MessageFileBean chatFile;
    // getter/setter
}

// 訊息附件
public class MessageFileBean implements Serializable {
    private int fileId;
    private String fileName, fileType, fileTitle;
    private long fileLength;
    private Byte[] fileByte;
    // getter/setter
}

伺服器端核心改造

public class ChatServer {
    private static ServerSocket server;
    private List<Socket> socketList = new ArrayList<>();
    private Map<Integer, Socket> socketMap = new HashMap<>();      // userId -> Socket
    private Map<Integer, UserInfoBean> userMap = new HashMap<>();   // userId -> UserInfo
    private Gson gson = new Gson();

    public void initServer() {
        server = new ServerSocket(8080);
        createMessage(); // 遞迴 accept + 開啟新線程持續監聽
    }

    private void createMessage() {
        Socket socket = server.accept();
        socketList.add(socket);
        new Thread(this::createMessage).start(); // 持續監聽新連線

        // 處理當前連線訊息
        new Thread(() -> {
            try (BufferedReader in = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()))) {
                String line;
                while ((line = in.readLine()) != null) {
                    MessageBean msg = gson.fromJson(line, MessageBean.class);
                    setChatMap(msg, socket);      // 註冊 userId -> socket 映射
                    getFriend(msg);               // 路由轉發給好友
                }
            } catch (Exception e) { e.printStackTrace(); }
        }).start();
    }

    // 註冊映射關係
    private void setChatMap(MessageBean msg, Socket socket) {
        if (!userMap.containsKey(msg.getUserId()))
            userMap.put(msg.getUserId(), mockUser(msg.getUserId()));
        if (!socketMap.containsKey(msg.getUserId()))
            socketMap.put(msg.getUserId(), socket);
    }

    // 精準路由轉發給好友
    private void getFriend(MessageBean msg) {
        Socket target = socketMap.get(msg.getFriendId());
        if (target != null) {
            try {
                String json = gson.toJson(msg) + "\n"; // 換行符作為訊息邊界
                target.getOutputStream().write(json.getBytes("utf-8"));
            } catch (IOException e) { e.printStackTrace(); }
        }
    }

    // 保留廣播功能(管理員推送)
    private void getMessage() { /* 從控制台讀取並廣播給所有 socketMap.values() */ }
}

客戶端關鍵邏輯

  • 登入區分用戶:admin/1 為用戶 1,admin/2 為用戶 2,啟動時設定 userId 與 friendId 互指
  • 發送訊息:Gson 序列化 + 換行符邊界,寫入 OutputStream
  • 接收訊息:子執行緒 BufferedReader.readLine() 阻塞讀取,反序列化後透過 Handler 更新 UI

關鍵設計點總結

  1. 用戶註冊機制:首次訊息攜帶 userId,伺服器建立 userId ↔ Socket 映射
  2. 訊息路由鍵:friendId 決定目標接收者,實現單聊
  3. 訊息邊界:統一使用換行符 \n 作為幀分隔,配合 readLine()
  4. 線程模型:主執行緒 accept() 監聽,每連線一執行緒處理 I/O,廣播另闢執行緒
  5. 擴展性:實體類預留 groupId、isGroup、chatType,支援群聊、多媒體擴展

此架構為即時通訊系統奠定基礎,後續可引入 Netty 重構提升併發效能,或整合 Redis 實現多節點訊息路由。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved