公司項目需要引入 WebSocket,調研主流容器支持情況。記錄關鍵發現與代碼示例。
版本差異:
代碼極簡(僅需 tomcat-embed-websocket.jar):
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/websocket")
public class WebSocketTest {
@OnMessage
public void onMessage(String message, Session session) throws IOException, InterruptedException {
System.out.println("Received: " + message);
session.getBasicRemote().sendText("First server message");
for (int i = 0; i < 3; i++) {
Thread.sleep(5000);
session.getBasicRemote().sendText("Intermediate message: " + i);
}
session.getBasicRemote().sendText("Last server message");
}
@OnOpen public void onOpen() { System.out.println("Client connected"); }
@OnClose public void onClose() { System.out.println("Connection closed"); }
}
核心差異:
| 特性 | Tomcat (BIO) | Jetty (NIO) |
|---|---|---|
| 線程模型 | 每連接一線程 | 少量線程處理海量連接 |
| WebSocket 適用性 | 連接數少時可接受 | 大規模長連接(聊天、推送)首選 |
| Servlet 基類 | HttpServlet | WebSocketServlet |
依賴: jetty-util.jar + jetty-websocket.jar
三類核心代碼結構:
// 1. Servlet 入口:繼承 WebSocketServlet
public class ReverseAjaxServlet extends WebSocketServlet {
private Endpoints endpoints = new Endpoints();
@Override public WebSocket doWebSocketConnect(HttpServletRequest req, String protocol) {
return endpoints.newEndpoint();
}
}
// 2. 連接管理器:廣播、註冊、移除
class Endpoints {
private Queue queue = new ConcurrentLinkedQueue<>();
public WebSocket newEndpoint() { return new Endpoint(this); }
void offer(Endpoint ep) { queue.offer(ep); }
void remove(Endpoint ep) { queue.remove(ep); }
void broadcast(String data) { /* 遍歷發送 */ }
}
// 3. 端點實現:OnTextMessage 接口
class Endpoint implements WebSocket.OnTextMessage {
private Connection conn;
public void onOpen(Connection c) { conn = c; endpoints.offer(this); }
public void onClose(int code, String msg) { endpoints.remove(this); }
public void onMessage(String data) { endpoints.broadcast(data); }
}
Chrome 擴展 WebSocket Client:輕鬆建立連接、發送消息、查看響應,無需寫客戶端代碼。