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

Java WebSocket 實作簡易點對點聊天室完整教學與程式碼範例

     編輯:Java 相關知識

前言

WebSocket 協定在 Java Web 開發中扮演著即時通訊的關鍵角色。本文將詳細介紹如何使用 Java WebSocket API 實作一個簡單的點對點聊天功能,包含伺服器端與前端的完整程式碼,適合初學者快速上手。

環境需求

  • JDK 7 或以上版本
  • 支援 WebSocket 的 Tomcat 容器(Tomcat 7.0.27+ 或 Tomcat 8+)

核心架構設計

本範例採用「Servlet 初始化儲存結構 + WebSocket Servlet 建立連線 + MessageInbound 處理訊息」的三層架構:

1. InitServlet - 使用者連線池管理

負責初始化一個靜態 HashMap,以使用者 ID 為鍵,MessageInbound 實例為值,作為線上使用者的查找表。

package socket;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.apache.catalina.websocket.MessageInbound;

public class InitServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static HashMap<String, MessageInbound> socketList;

    @Override
    public void init(ServletConfig config) throws ServletException {
        socketList = new HashMap<>();
        super.init(config);
        System.out.println("伺服器啟動,WebSocket 連線池已初始化");
    }

    public static HashMap<String, MessageInbound> getSocketList() {
        return socketList;
    }
}

2. MyWebSocketServlet - WebSocket 連線進入點

繼承 WebSocketServlet,在建立連線時從 Session 取得使用者 ID,並建立對應的 MyMessageInbound 實例。

package socket;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;

public class MyWebSocketServlet extends WebSocketServlet {
    private String getUser(HttpServletRequest request) {
        return (String) request.getSession().getAttribute("user");
    }

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
        return new MyMessageInbound(getUser(request));
    }
}

3. MyMessageInbound - 訊息處理核心

處理連線開啟、文字訊息接收與發送、連線關閉等生命週期事件。關鍵邏輯在 onTextMessage 中解析訊息格式「發送者,接收者,內容」,並透過 InitServlet 的 socketList 精準推送給目標使用者。

package socket;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.HashMap;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;
import util.MessageUtil;

public class MyMessageInbound extends MessageInbound {
    private String userId;

    public MyMessageInbound(String userId) {
        this.userId = userId;
    }

    @Override
    protected void onOpen(WsOutbound outbound) {
        super.onOpen(outbound);
        if (userId != null) {
            InitServlet.getSocketList().put(userId, this);
            System.out.println("使用者上線:" + userId);
        }
    }

    @Override
    protected void onTextMessage(CharBuffer msg) throws IOException {
        HashMap<String, String> messageMap = MessageUtil.getMessage(msg);
        String fromName = messageMap.get("fromName");
        String toName = messageMap.get("toName");
        String content = messageMap.get("content");

        MessageInbound target = InitServlet.getSocketList().get(toName);
        if (target != null) {
            WsOutbound outbound = target.getWsOutbound();
            String formattedMsg = fromName + ":" + content;
            outbound.writeTextMessage(CharBuffer.wrap(formattedMsg.toCharArray()));
        }
    }

    @Override
    protected void onClose(int status) {
        if (userId != null) {
            InitServlet.getSocketList().remove(userId);
            System.out.println("使用者下線:" + userId);
        }
        super.onClose(status);
    }

    @Override
    protected void onBinaryMessage(ByteBuffer arg) throws IOException {
        // 二進位訊息處理(本範例不使用)
    }
}

4. MessageUtil - 訊息解析工具

將前端傳來的字串依逗號分割為結構化 Map。

package util;
import java.nio.CharBuffer;
import java.util.HashMap;

public class MessageUtil {
    public static HashMap<String, String> getMessage(CharBuffer msg) {
        HashMap<String, String> map = new HashMap<>();
        String[] parts = msg.toString().split(",", 3);
        map.put("fromName", parts[0]);
        map.put("toName", parts.length > 1 ? parts[1] : "");
        map.put("content", parts.length > 2 ? parts[2] : "");
        return map;
    }
}

Web.xml 部署設定

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="https://java.sun.com/xml/ns/javaee"
    xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <servlet>
        <servlet-name>mywebsocket</servlet-name>
        <servlet-class>socket.MyWebSocketServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>mywebsocket</servlet-name>
        <url-pattern>/mywebsocket.do</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>initServlet</servlet-name>
        <servlet-class>socket.InitServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

前端頁面實作

提供兩個 JSP 頁面模擬不同使用者登入(小華、小明),透過 JavaScript WebSocket API 進行雙向通訊。

chat_xiaohua.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<% session.setAttribute("user", "小華"); %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>WebSocket 聊天室 - 小華</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        var ws;
        function connect() {
            var url = "ws://" + location.host + "/webSocket/mywebsocket.do";
            ws = new WebSocket(url);
            
            ws.onopen = function() {
                $('#status').text("線上").css("color", "green");
                $('#userName').text("小華");
            };
            
            ws.onmessage = function(evt) {
                $('#msgArea').val(function(i, v) { return v + evt.data + "\n"; });
            };
            
            ws.onclose = function() {
                $('#status').text("離線").css("color", "red");
            };
            
            ws.onerror = function(err) {
                console.error("WebSocket 錯誤:", err);
            };
        }
        
        function sendMsg() {
            var to = $('#targetUser').val();
            var content = $('#inputMsg').val();
            if (content.trim()) {
                ws.send("小華," + to + "," + content);
                $('#inputMsg').val('');
            }
        }
        
        $(function() { connect(); });
    </script>
</head>
<body onload="connect()">
    <h3>WebSocket 點對點聊天測試</h3>
    狀態:<span id="status">連線中...</span><br>
    登入身分:<span id="userName"></span><br><br>
    傳送給:<input type="text" id="targetUser" value="小明"><br>
    訊息內容:<input type="text" id="inputMsg" style="width:300px">
    <button onclick="sendMsg()">發送</button><br><br>
    聊天記錄:<br>
    <textarea id="msgArea" rows="15" cols="60" readonly></textarea>
</body>
</html>

chat_xiaoming.jsp

同上,僅將 session user 設為「小明」,預設傳送目標改為「小華」。

測試與驗證

  1. 將專案部署至 Tomcat
  2. 開啟兩個瀏覽器分別訪問 chat_xiaohua.jsp 與 chat_xiaoming.jsp
  3. 觀察狀態顯示「線上」
  4. 雙向發送訊息,驗證即時送達

常見問題排查

  • 連線失敗:確認 Tomcat 版本支援 WebSocket、防火牆未阻擋端口、URL 路徑正確
  • 訊息收不到:檢查 session 屬性名稱是否一致、MessageUtil 解析邏輯是否符合前端發送格式
  • 中文亂碼:確保 JSP、Java 檔案、資料庫皆統一使用 UTF-8 編碼

擴充建議

此為基礎版本,實務應用可考慮加入:心跳機制維持長連線、訊息持久化儲存、群組聊天支援、使用者上下線通知、訊息已讀狀態、SSL/TLS 加密傳輸等功能。

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