检测异常断开
- 心跳机制:
- 原理:客户端和服务器定期互相发送心跳消息。如果一方在一定时间内没有收到对方的心跳消息,就认为连接可能断开。
- 关键类和方法:在Java的WebSocket实现(如Tyrus、Netty等)中,可自定义心跳消息。以Tyrus为例,可通过
Session.getBasicRemote().sendText("heartbeat")
发送心跳消息。在服务器端,通过@OnMessage
注解的方法接收心跳消息并处理。
- 监听器机制:
- 原理:利用WebSocket的监听器,当连接关闭时,监听器会收到通知。
- 关键类和方法:在Tyrus中,可实现
Endpoint
类,重写onClose
方法,在连接关闭时进行处理。在Spring WebSocket中,可实现WebSocketHandler
接口,重写afterConnectionClosed
方法。
重连机制
- 简单重连:
- 逻辑:检测到连接断开后,立即尝试重新连接。
- 关键代码:
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Session;
import java.net.URI;
import java.net.URISyntaxException;
@ClientEndpoint
public class WebSocketClient {
private Session session;
private static final String SERVER_URI = "ws://localhost:8080/websocket";
public WebSocketClient() {
connect();
}
private void connect() {
try {
session = ContainerProvider.getWebSocketContainer().connectToServer(this, new URI(SERVER_URI));
} catch (DeploymentException | URISyntaxException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Connection closed: " + closeReason);
connect();
}
}
- 带延迟的重连:
- 逻辑:检测到连接断开后,延迟一段时间再尝试重连,每次重连失败后延迟时间可适当增加。
- 关键代码:
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Session;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ClientEndpoint
public class WebSocketClient {
private Session session;
private static final String SERVER_URI = "ws://localhost:8080/websocket";
private int reconnectDelay = 5; // 初始延迟5秒
private static final int MAX_RECONNECT_DELAY = 60; // 最大延迟60秒
private ScheduledExecutorService executorService;
public WebSocketClient() {
connect();
}
private void connect() {
try {
session = ContainerProvider.getWebSocketContainer().connectToServer(this, new URI(SERVER_URI));
reconnectDelay = 5;
if (executorService != null) {
executorService.shutdown();
}
} catch (DeploymentException | URISyntaxException e) {
scheduleReconnect();
}
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Connection closed: " + closeReason);
scheduleReconnect();
}
private void scheduleReconnect() {
if (executorService == null || executorService.isShutdown()) {
executorService = Executors.newSingleThreadScheduledExecutor();
}
executorService.schedule(() -> {
connect();
reconnectDelay = Math.min(reconnectDelay * 2, MAX_RECONNECT_DELAY);
}, reconnectDelay, TimeUnit.SECONDS);
}
}