技術棧版本要求
- Java 7+(JSR-356 標準 API)
- Tomcat 7+ / Jetty 9.1+(容器實作)
- Spring 4.0+(Spring WebSocket 支援)
一、原生 JSR-356 客戶端
@ClientEndpoint
public class SimpleClient {
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected: " + session.getId());
}
@OnMessage
public void onMessage(String msg) { System.out.println("收到: " + msg); }
@OnError
public void onError(Throwable t) { t.printStackTrace(); }
}
// 發起連線
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(SimpleClient.class, URI.create("ws://localhost:8081/log/log"));
session.getBasicRemote().sendText("Hello Server");
二、Spring 普通 WebSocket 端點
配置類
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(logHandler(), "/log");
}
@Bean
public WebSocketHandler logHandler() {
return new LogWebSocketHandler();
}
}
處理器
public class LogWebSocketHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("Server 收到: " + payload);
session.sendMessage(new TextMessage("Echo: " + payload));
}
}
三、Spring STOMP over WebSocket(訊息代理模式)
啟用訊息代理配置
@Configuration
@EnableWebSocketMessageBroker
public class StompConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS(); // 支援 SockJS 降級
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic"); // 簡易訊息代理
registry.setApplicationDestinationPrefixes("/app");
}
}
Controller 處理訂閱/廣播
@Controller
public class GreetingController {
@MessageMapping("/hello") // 客戶端發送到 /app/hello
@SendTo("/topic/greetings") // 廣播到訂閱 /topic/greetings 的客戶端
public Greeting greet(HelloMessage msg) throws Exception {
Thread.sleep(1000); // 模擬處理
return new Greeting("Hello, " + msg.getName() + "!");
}
}
四、瀏覽器端 SockJS + STOMP.js
var socket = new SockJS('/log/hello');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/greetings', function(greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({ 'name': document.getElementById('name').value }));
}
五、關鍵協議細節
- STOMP 幀格式:`COMMAND\nheader:value\n\nbody\0`
- SockJS 提供 WebSocket 不可用時的 HTTP 長輪詢/Streaming 降級
- Spring `SimpMessagingTemplate` 可在任意處廣播:`template.convertAndSend("/topic/notify", "訊息")`
選型建議
| 場景 | 推薦方案 |
| 簡單雙向通訊、低延遲 | 原生 JSR-356 / Spring TextWebSocketHandler |
| 複雜訊息路由、廣播、訂閱、權限 | Spring STOMP + 簡易/外部 Broker (RabbitMQ/Kafka) |
| 瀏覽器相容性要求高 | SockJS 降級 |