如何检查 Websocket 连接是否有效
How to check is a Websocket connection is alive
我有一个到服务器的 websocket 连接:
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@ClientEndpoint
public class WebsocketExample {
private Session userSession;
private void connect() {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI("someaddress"));
} catch (DeploymentException | URISyntaxException | IOException e) {
e.printStackTrace();
}
}
@OnOpen
public void onOpen(Session userSession) {
// Set the user session
this.userSession = userSession;
System.out.println("Open");
}
@OnClose
public void onClose(Session userSession, CloseReason reason) {
this.userSession = null;
System.out.println("Close");
}
@OnMessage
public void onMessage(String message) {
// Do something with the message
System.out.println(message);
}
}
一段时间后,我似乎没有再收到来自服务器的任何消息,但是没有调用 onClose 方法。
我想要一种计时器,如果我在过去五分钟内没有收到任何消息,至少会记录一个错误(并且最多尝试重新连接)。当我收到一条新消息时,计时器将被重置。
我该怎么做?
这是我所做的。我通过 jetty 更改了 javax.websocket 并实现了一个 ping 调用:
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@WebSocket
public class WebsocketExample {
private Session userSession;
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private void connect() {
try {
SslContextFactory sslContextFactory = new SslContextFactory();
WebSocketClient client = new WebSocketClient(sslContextFactory);
client.start();
client.connect(this, new URI("Someaddress"));
} catch (Exception e) {
e.printStackTrace();
}
}
@OnWebSocketConnect
public void onOpen(Session userSession) {
// Set the user session
this.userSession = userSession;
System.out.println("Open");
executorService.scheduleAtFixedRate(() -> {
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
userSession.getRemote().sendPing(payload);
} catch (IOException e) {
e.printStackTrace();
}
},
5, 5, TimeUnit.MINUTES);
}
@OnWebSocketClose
public void onClose(int code, String reason) {
this.userSession = null;
System.out.println("Close");
}
@OnWebSocketMessage
public void onMessage(String message) {
// Do something with the message
System.out.println(message);
}
}
编辑:这只是一个 ping 示例...我不知道是否所有服务器都应该通过 pong 来回答...
Edit2: 下面是如何处理pong消息。诀窍不是监听字符串消息,而是监听帧消息:
@OnWebSocketFrame
@SuppressWarnings("unused")
public void onFrame(Frame pong) {
if (pong instanceof PongFrame) {
lastPong = Instant.now();
}
}
为了管理服务器超时,我修改了计划任务如下:
scheduledFutures.add(executorService.scheduleAtFixedRate(() -> {
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
userSession.getRemote().sendPing(payload);
if (lastPong != null
&& Instant.now().getEpochSecond() - lastPong.getEpochSecond() > 60) {
userSession.close(1000, "Timeout manually closing dead connection.");
}
} catch (IOException e) {
e.printStackTrace();
}
},
10, 10, TimeUnit.SECONDS));
...并在onClose方法中处理重连
您应该通过实现一个心跳系统来解决这个问题,该系统一侧发送 ping
,另一侧用 pong
应答。几乎每个 websocket 客户端和服务器(据我所知)都在内部支持此功能。这个 ping/pong 帧可以从双方发送。我通常在服务器端实现它,因为我通常知道它比客户端更有机会存活下来(我的意见)。如果客户端长时间不发回 pong,我知道连接已断开。在客户端,我检查同样的:如果服务器很长时间没有发送 ping 消息,我知道连接已经死了。
如果 ping/pong 没有在您使用的库中实现(我认为 javax websocket 有),您可以为此制定自己的协议。
我有一个到服务器的 websocket 连接:
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@ClientEndpoint
public class WebsocketExample {
private Session userSession;
private void connect() {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI("someaddress"));
} catch (DeploymentException | URISyntaxException | IOException e) {
e.printStackTrace();
}
}
@OnOpen
public void onOpen(Session userSession) {
// Set the user session
this.userSession = userSession;
System.out.println("Open");
}
@OnClose
public void onClose(Session userSession, CloseReason reason) {
this.userSession = null;
System.out.println("Close");
}
@OnMessage
public void onMessage(String message) {
// Do something with the message
System.out.println(message);
}
}
一段时间后,我似乎没有再收到来自服务器的任何消息,但是没有调用 onClose 方法。
我想要一种计时器,如果我在过去五分钟内没有收到任何消息,至少会记录一个错误(并且最多尝试重新连接)。当我收到一条新消息时,计时器将被重置。
我该怎么做?
这是我所做的。我通过 jetty 更改了 javax.websocket 并实现了一个 ping 调用:
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@WebSocket
public class WebsocketExample {
private Session userSession;
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private void connect() {
try {
SslContextFactory sslContextFactory = new SslContextFactory();
WebSocketClient client = new WebSocketClient(sslContextFactory);
client.start();
client.connect(this, new URI("Someaddress"));
} catch (Exception e) {
e.printStackTrace();
}
}
@OnWebSocketConnect
public void onOpen(Session userSession) {
// Set the user session
this.userSession = userSession;
System.out.println("Open");
executorService.scheduleAtFixedRate(() -> {
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
userSession.getRemote().sendPing(payload);
} catch (IOException e) {
e.printStackTrace();
}
},
5, 5, TimeUnit.MINUTES);
}
@OnWebSocketClose
public void onClose(int code, String reason) {
this.userSession = null;
System.out.println("Close");
}
@OnWebSocketMessage
public void onMessage(String message) {
// Do something with the message
System.out.println(message);
}
}
编辑:这只是一个 ping 示例...我不知道是否所有服务器都应该通过 pong 来回答...
Edit2: 下面是如何处理pong消息。诀窍不是监听字符串消息,而是监听帧消息:
@OnWebSocketFrame
@SuppressWarnings("unused")
public void onFrame(Frame pong) {
if (pong instanceof PongFrame) {
lastPong = Instant.now();
}
}
为了管理服务器超时,我修改了计划任务如下:
scheduledFutures.add(executorService.scheduleAtFixedRate(() -> {
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
userSession.getRemote().sendPing(payload);
if (lastPong != null
&& Instant.now().getEpochSecond() - lastPong.getEpochSecond() > 60) {
userSession.close(1000, "Timeout manually closing dead connection.");
}
} catch (IOException e) {
e.printStackTrace();
}
},
10, 10, TimeUnit.SECONDS));
...并在onClose方法中处理重连
您应该通过实现一个心跳系统来解决这个问题,该系统一侧发送 ping
,另一侧用 pong
应答。几乎每个 websocket 客户端和服务器(据我所知)都在内部支持此功能。这个 ping/pong 帧可以从双方发送。我通常在服务器端实现它,因为我通常知道它比客户端更有机会存活下来(我的意见)。如果客户端长时间不发回 pong,我知道连接已断开。在客户端,我检查同样的:如果服务器很长时间没有发送 ping 消息,我知道连接已经死了。
如果 ping/pong 没有在您使用的库中实现(我认为 javax websocket 有),您可以为此制定自己的协议。