Tổng hợp kiến thức về WebSocket trong Spring Boot
1. Cấu hình WebSocket
Thêm dependency
Truy cập Maven Central, tìm kiếm spring-boot-starter-websocket, chọn phiên bản phù hợp, và thêm dependency vào file pom.xml trong thẻ dependencies.
Config WebSocket
Tạo một lớp cấu hình WebSocketConfig và kích hoạt hỗ trợ WebSocket:
Lớp này có các chức năng chính:
- Kích hoạt hỗ trợ WebSocket: Lớp cấu hình này trả về một thể hiện của
ServerEndpointExporterđể kích hoạt hỗ trợ WebSocket. - Tự động đăng ký endpoint: Nó tự động đăng ký các WebSocket endpoint được đánh dấu bằng annotation
@ServerEndpoint, cho phép chúng xử lý các yêu cầu WebSocket.
package com.vietnamese.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
2. Tạo WebSocket endpoint
Tạo một WebSocket endpoint ChatWebSocketHandler để xử lý giao tiếp giữa client và server.
Khi một user kết nối socket đến server, một thể hiện của lớp ChatWebSocketHandler sẽ được tạo.
Khái niệm Session sẽ được giải thích ở cuối bài.
package com.vietnamese.websocket.handler;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
@Component
@ServerEndpoint("/chat/{userId}")
public class ChatWebSocketHandler {
private Session currentSession;
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.currentSession = session;
System.out.println("User connected with ID: " + userId);
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("Received message: " + message);
try {
session.getBasicRemote().sendText("Server received: " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session) {
System.out.println("Session closed");
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
}
3. Cấu hình Spring Security (tùy chọn)
Nếu ứng dụng của bạn sử dụng Spring Security, bạn có thể cần cấu hình để bỏ qua kiểm tra bảo mật cho WebSocket endpoint:
Bạn cần thêm nội dung sau vào lớp SecurityConfig của mình để server có thể nhận được các yêu cầu WebSocket. SecurityConfig là một lớp tùy chỉnh để quản lý Spring Security.
package com.vietnamese.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
@Configuration
public class SecurityConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/chat/**");
}
}
4. Code phía client
Bạn có thể sử dụng JavaScript ở phía front-end để giao tiếp với WebSocket server.
<script>
let websocketConnection = null;
function establishConnection() {
let userId = "user123";
// @ServerEndpoint("/chat/{userId}")
// Ở đây cũng có thể truyền các tham số khác, ví dụ token,
// chúng sẽ được nhận bởi annotation ở phía server
websocketConnection = new WebSocket('ws://localhost:8080/chat/' + userId);
// Hàm này được gọi khi kết nối được thiết lập
websocketConnection.onopen = function(event) {
// Xử lý khi kết nối thành công
};
// Hàm này được gọi khi nhận được tin nhắn từ server
websocketConnection.onmessage = function(event) {
// Xử lý tin nhận nhận được
};
// Hàm này được gọi khi kết nối đóng
websocketConnection.onclose = function(event) {
// Xử lý khi kết nối đóng
};
// Hàm này được gọi khi có lỗi
websocketConnection.onerror = function(event) {
// Xử lý lỗi
};
// Có thể đóng kết nối thủ công,
// hàm này sẽ gọi onclose(), thường dùng trong Vue khi component bị hủy
websocketConnection.close();
// Gửi tin nhắn đến server, nên dùng định dạng JSON, JSON.stringify()
websocketConnection.sendMessage(jsonData)
}
</script>
5. Xử lý tin nhắn JSON
Trong quá trình trao đổi thông tin, chúng ta thường sử dụng định dạng JSON. Thư viện chuẩn Java không chứa chức năng xử lý JSON trực tiếp. Do đó, cần引入 thư viện bên thứ ba để xử lý dữ liệu JSON.
Ở đây chúng ta sẽ sử dụng fastjson do Alibaba cung cấp. Truy cập Maven Central, tìm kiếm fastjson, chọn phiên bản phù hợp và thêm vào dependencies trong file pom.xml.
// Serialize Java object to JSON string
String jsonString = JSON.toJSONString(userObject);
// Deserialize JSON string to Java object
User user = JSON.parseObject(jsonString, User.class);
// Create a JSONObject
JSONObject json = new JSONObject();
json.put("name", "John");
// Convert to JSON string
json.toString()
// Parse JSON string
String jsonData = "{\"name\":\"Alice\",\"age\":25,\"city\":\"Hanoi\"}";
JSONObject parsedObject = JSONObject.parseObject(jsonData);
// Get value, output Alice
System.out.println("Name: " + parsedObject.getString("name"));
6. Session
Session trong WebSocket là một khái niệm quan trọng, nó đại diện cho kết nối giữa client và server. Thông qua đối tượng Session, bạn có thể gửi và nhận tin nhắn, lấy thông tin kết nối, quản lý trạng thái kết nối và xử lý lỗi trong kết nối.
1. Tạo WebSocket server endpoint
Bước đầu tiên khi sử dụng WebSocket trong Java là tạo một WebSocket server endpoint. Bằng cách sử dụng annotation @ServerEndpoint, bạn có thể định nghĩa WebSocket server endpoint và xử lý các sự kiện kết nối, đóng kết nối, tin nhắn và lỗi.
Ví dụ:
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/chat")
public class ChatEndpoint {
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("Received: " + message);
session.getBasicRemote().sendText("Echo: " + message);
}
@OnClose
public void onClose(Session session) {
System.out.println("Disconnected: " + session.getId());
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
}
2. Các phương thức thường dùng của Session
- Lấy ID duy nhất của kết nối:
session.getId() - Gửi tin nhắn:
- Gửi tin nhắn văn bản:
session.getBasicRemote().sendText("message")- Gửi tin nhắn nhị phân:session.getBasicRemote().sendBinary(ByteBuffer buffer) - Lấy thông tin cơ bản của kết nối:
- Lấy URI của kết nối:
session.getRequestURI()- Lấy tham số của kết nối:session.getPathParameters()- Lấy tham số yêu cầu của kết nối:session.getRequestParameterMap() - Đóng kết nối:
session.close() - Kiểm tra trạng thái kết nối:
session.isOpen()
3. Xử lý tin nhắn
Trong WebSocket, xử lý tin nhắn được thực hiện thông qua phương thức được đánh dấu annotation @OnMessage. Trong phương thức này, bạn có thể xử lý tin nhắn từ client và sử dụng đối tượng Session để gửi phản hồi.
Ví dụ:
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("Received: " + message);
// Gửi tin nhắn echo về client
session.getBasicRemote().sendText("Echo: " + message);
}
4. Xử lý kết nối và ngắt kết nối
Phương thức được đánh dấu annotation @OnOpen và @OnClose lần lượt được sử dụng để xử lý sự kiện thiết lập kết nối và ngắt kết nối. Trong các phương thức này, bạn có thể thực hiện một số thao tác khởi tạo hoặc dọn dẹp.
Ví dụ:
@OnOpen
public void onOpen(Session session) {
System.out.println("Connected: " + session.getId());
}
@OnClose
public void onClose(Session session) {
System.out.println("Disconnected: " + session.getId());
}
5. Xử lý lỗi
Phương thức được đánh dấu annotation @OnError được sử dụng để xử lý lỗi trong kết nối WebSocket. Trong phương thức này, bạn có thể ghi lại thông tin lỗi hoặc thực hiện các thao xử lý lỗi khác.
Ví dụ:
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
6. Sử dụng Session cho các thao tác nâng cao
6.1 Lưu và chia sẻ trạng thái user
Trong ứng dụng WebSocket, bạn có thể sử dụng đối tượng Session để lưu và chia sẻ trạng thái user. Ví dụ, bạn có thể lưu thông tin xác thực của user vào đối tượng Session và sử dụng chúng trong quá trình xử lý tin nhắn sau này.
Ví dụ:
@OnOpen
public void onOpen(Session session) {
session.getUserProperties().put("username", "John");
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
String username = (String) session.getUserProperties().get("username");
session.getBasicRemote().sendText("Hello, " + username + "! You sent: " + message);
}
6.2 Broadcast tin nhắn
Bạn có thể sử dụng tập hợp các đối tượng Session để thực hiện broadcast tin nhắn, tức là gửi tin nhắn đến tất cả các client kết nối.
Ví dụ:
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@ServerEndpoint("/chat")
public class ChatServer {
private static Set<Session> activeSessions = Collections.synchronizedSet(new HashSet<>());
@OnOpen
public void onOpen(Session session) {
activeSessions.add(session);
System.out.println("Connected: " + session.getId());
}
@OnClose
public void onClose(Session session) {
activeSessions.remove(session);
System.out.println("Disconnected: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
for (Session s : activeSessions) {
if (s.isOpen()) {
s.getBasicRemote().sendText("Broadcast: " + message);
}
}
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
}