问题
I managed to create a WebSocketHandler
using the Spring 5 Reactive WebSocket support (Chapter 23.2.4). Receiving and sending all works fine. However, I can not figure out how to detect a client disconnect. When debugging a client disconnect it somewhere stops at the server side in the HttpServerWSOperations
class (netty.http.server
package), where it does detect a CloseWebSocketFrame
.
Any suggestions how to deal with client disconnects?
回答1:
I implemented a close event handler in a reactive org.springframework.web.reactive.socket.WebSocketHandler as follows:
public Mono<Void> handle(final WebSocketSession session) {
final String sessionId = session.getId();
if(sessions.add(sessionId)) { // add session id to set to keep a count of active sessions
LOG.info("Starting WebSocket Session [{}]", sessionId);
// Send the session id back to the client
WebSocketMessage msg = session.textMessage(String.format("{\"session\":\"%s\"}", sessionId));
// Register the outbound flux as the source of outbound messages
final Flux<WebSocketMessage> outFlux = Flux.concat(Flux.just(msg), newMetricFlux.map(metric -> {
LOG.info("Sending message to client [{}]: {}", sessionId, metric);
return session.textMessage(metric);
}));
// Subscribe to the inbound message flux
session.receive().doFinally(sig -> {
LOG.info("Terminating WebSocket Session (client side) sig: [{}], [{}]", sig.name(), sessionId);
session.close();
sessions.remove(sessionId); // remove the stored session id
}).subscribe(inMsg -> {
LOG.info("Received inbound message from client [{}]: {}", sessionId, inMsg.getPayloadAsText());
});
return session.send(outFlux);
}
return Mono.empty();
}
The newMetricFlux
field is the source of the outbound websocket messages. The trick to hooking the close event is the doFinally on the inbound message flux. When the websocket client closes, the inbound flux is terminated.
For some reason, though, there is a 1 minute delay between when the netty channel closes and the doFinally
callback is executed. Not sure why yet.
Here's the log output for a browser client connecting and immediately closing. Note the 60 second delay between lines 3 and 4.
2017-08-03 11:15:41.177 DEBUG 28505 --- [ctor-http-nio-2] r.i.n.http.server.HttpServerOperations : New http connection, requesting read
2017-08-03 11:15:41.294 INFO 28505 --- [ctor-http-nio-2] c.h.w.ws.NewMetricsWebSocketHandler : Starting WebSocket Session [87fbe66]
2017-08-03 11:15:48.294 DEBUG 28505 --- [ctor-http-nio-2] r.i.n.http.server.HttpServerOperations : CloseWebSocketFrame detected. Closing Websocket
2017-08-03 11:16:48.293 INFO 28505 --- [ctor-http-nio-2] c.h.w.ws.NewMetricsWebSocketHandler : Terminating WebSocket Session (client side) sig: [ON_COMPLETE], [87fbe66]
Update: October 13, 2017:
As of Spring 5 GA, the delay mentioned above is not present and I observe my callback being invoked immediately after the client is closed. Not sure which version this was fixed in, but as I said, it's fixed in 5.0 GA.
回答2:
You can detect that thanks to the afterConnectionClosed
method. You can even handle transport errors thanks to the method handleTransportError
. Here is a code snippet:
@Component
public class YourHandler extends TextWebSocketHandler {
private final static Logger logger = LoggerFactory.getLogger(YourHandler .class);
private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
logger.debug("Connected : " + session);
sessions.add(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
try {
//check the close status if you want...
if (!status.equals(CloseStatus.NORMAL)) {
session.close();
}
} catch (IOException e) {
logger.error("Cannot close session on afterConnectionClosed ", e);
}
sessions.remove(session);
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) {
logger.debug("error has occured with the following session {}", session);
try {
session.close();
} catch (IOException e) {
logger.error("Cannot close session on handleTransportError ", e);
}
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.debug("Receive : " + message.getPayload());
}
}
来源:https://stackoverflow.com/questions/45038008/how-can-i-detect-a-disconnected-client-with-spring-5-reactive-websocket