瀏覽器安全沙箱禁止 JavaScript 直接建立 TCP Socket。必須使用 WebSocket (ws:// / wss://) 協定,經過 HTTP Upgrade 握手後雙工通訊。
瀏覽器 (JS WebSocket) ⟷ HTTP Upgrade ⟷ Java 伺服器 (Netty / Spring WebSocket / JSR-356)
// 1. 依賴
implementation 'org.springframework.boot:spring-boot-starter-websocket'
// 2. 設定
@Configuration
@EnableWebSocketMessageBroker
public class WsConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS(); // 支援 fallback
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic"); // 訂閱前綴
registry.setApplicationDestinationPrefixes("/app"); // 發送前綴
}
}
// 3. Controller
@Controller
public class WsController {
@MessageMapping("/chat") // 客戶端送 /app/chat
@SendTo("/topic/messages") // 廣播到 /topic/messages
public String handle(String msg) {
return "Server echo: " + msg;
}
}
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = () => console.log('已連線');
ws.onmessage = e => {
const div = document.createElement('div');
div.textContent = e.data;
document.getElementById('log').appendChild(div);
};
ws.onerror = e => console.error(e);
ws.onclose = () => console.log('斷線');
function send() {
const input = document.getElementById('msg');
ws.send(input.value);
input.value = '';
}
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) {
System.out.println("連線: " + session.getId());
}
@OnMessage
public void onMessage(String msg, Session session) {
session.getAsyncRemote().sendText("Echo: " + msg);
}
@OnClose
public void onClose(Session session) {
System.out.println("斷線: " + session.getId());
}
}
前端連線:ws://host:port/你的context-path/echo
| 問題 | 解決 |
|---|---|
| 防火牆/Proxy 封鎖 ws | 使用 wss:// (TLS) + 標準 443 port |
| 斷線重連 | 前端實作指數退避重連邏輯 |
| 訊息堆積 | 後端加入背壓控制 / 訊息隊列 |
| CORS | Spring: registry.setAllowedOrigins("*") |