/** * When a session is connected through a higher-level protocol it has a chance * to use heartbeat management to shut down sessions that are too slow to send * or receive messages. However, after a WebSocketSession is established and * before the higher level protocol is fully connected there is a possibility * for sessions to hang. This method checks and closes any sessions that have * been connected for more than 60 seconds without having received a single * message. */ private void checkSessions() throws IOException { long currentTime = System.currentTimeMillis(); if (!isRunning() || (currentTime - this.lastSessionCheckTime < TIME_TO_FIRST_MESSAGE)) { return; } if (this.sessionCheckLock.tryLock()) { try { for (WebSocketSessionHolder holder : this.sessions.values()) { if (holder.hasHandledMessages()) { continue; } long timeSinceCreated = currentTime - holder.getCreateTime(); if (timeSinceCreated < TIME_TO_FIRST_MESSAGE) { continue; } WebSocketSession session = holder.getSession(); if (logger.isErrorEnabled()) { logger.error("No messages received after " + timeSinceCreated + " ms. " + "Closing " + holder.getSession() + "."); } try { this.stats.incrementNoMessagesReceivedCount(); session.close(CloseStatus.SESSION_NOT_RELIABLE); } catch (Throwable t) { logger.error("Failure while closing " + session, t); } } } finally { this.sessionCheckLock.unlock(); } } }
/** Handle an outbound Spring Message to a WebSocket client. */ @Override public void handleMessage(Message<?> message) throws MessagingException { String sessionId = resolveSessionId(message); if (sessionId == null) { logger.error("Couldn't find sessionId in " + message); return; } WebSocketSessionHolder holder = this.sessions.get(sessionId); if (holder == null) { if (logger.isDebugEnabled()) { // The broker may not have removed the session yet logger.debug("No session for " + message); } return; } WebSocketSession session = holder.getSession(); try { findProtocolHandler(session).handleMessageToClient(session, message); } catch (SessionLimitExceededException ex) { try { if (logger.isDebugEnabled()) { logger.debug("Terminating '" + session + "'", ex); } this.stats.incrementLimitExceededCount(); clearSession(session, ex.getStatus()); // clear first, session may be unresponsive session.close(ex.getStatus()); } catch (Exception secondException) { logger.debug("Failure while closing session " + sessionId + ".", secondException); } } catch (Exception e) { // Could be part of normal workflow (e.g. browser tab closed) logger.debug("Failed to send message to client in " + session + ": " + message, e); } }
/** 给特定的在线用户发送消息 */ public void sendMessageToUsers(ArrayList<Long> userIdList, TextMessage message) { if (userIdList == null) { return; } int listSize = userIdList.size(); if (listSize == 0) { return; } int i = 0; for (WebSocketSession user : users) { try { if (user.getAttributes().get("USER_ID") != null) { if (user.isOpen() && userIdList.contains(user.getAttributes().get("USER_ID"))) { user.sendMessage(message); i++; if (listSize == i) { break; } } } } catch (IOException e) { e.printStackTrace(); } } }
/** 会话抛出异常 */ @Override public void handleTransportError(WebSocketSession session, Throwable arg1) throws Exception { // TODO Auto-generated method stub if (session.isOpen()) { session.close(); } WebSocketCache.removeMemberSession(session); }
@Override public void handleTransportError(WebSocketSession session, Throwable exception) throws IOException { if (session.isOpen()) { session.close(); } users.remove(session); }
private void clearSession(WebSocketSession session, CloseStatus closeStatus) throws Exception { if (logger.isDebugEnabled()) { logger.debug("Clearing session " + session.getId()); } if (this.sessions.remove(session.getId()) != null) { this.stats.decrementSessionCount(session); } findProtocolHandler(session).afterSessionEnded(session, closeStatus, this.clientInboundChannel); }
@Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class); log.debug("Incoming message: {}", jsonMessage); switch (jsonMessage.get("id").getAsString()) { case "start": start(session, jsonMessage); break; case "show_faces": setVisualization(session, jsonMessage); break; case "scale_factor": setScaleFactor(session, jsonMessage); break; case "process_num_frames": setProcessNumberFrames(session, jsonMessage); break; case "width_to_process": setWidthToProcess(session, jsonMessage); break; case "get_stats": getStats(session); break; case "euclidean_dis": setEuclideanDistance(session, jsonMessage); break; case "area_threshold": setAreaThreshold(session, jsonMessage); break; case "track_treshold": setTrackThreshold(session, jsonMessage); break; case "stop": { UserSession user = users.remove(session.getId()); if (user != null) { user.release(); } break; } case "onIceCandidate": { JsonObject candidate = jsonMessage.get("candidate").getAsJsonObject(); UserSession user = users.get(session.getId()); if (user != null) { user.addCandidate(candidate); } break; } default: error(session, "Invalid message with id " + jsonMessage.get("id").getAsString()); break; } }
@Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { WebSocketSession removed = IntegrationWebSocketContainer.this.sessions.remove(session.getId()); if (removed != null) { IntegrationWebSocketContainer.this.sessions.remove(session.getId()); } throw new Exception(exception); }
/** 给所有在线用户发送消息 */ public void sendMessageToUsers(TextMessage message) { for (WebSocketSession user : users) { try { if (user.isOpen()) { user.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } } }
@Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { List<WebSocketSession> sessions = Sessions.getSessions(); for (WebSocketSession webSocketSession : sessions) { try { webSocketSession.sendMessage(new TextMessage(message.getPayload())); } catch (IOException e) { e.printStackTrace(); } } }
@Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // WebSocketHandlerDecorator could close the session if (!session.isOpen()) { return; } this.stats.incrementSessionCount(session); session = new ConcurrentWebSocketSessionDecorator(session, getSendTimeLimit(), getSendBufferSizeLimit()); this.sessions.put(session.getId(), new WebSocketSessionHolder(session)); findProtocolHandler(session).afterSessionStarted(session, this.clientInboundChannel); }
@Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class); log.debug("Incoming message: {}", jsonMessage); switch (jsonMessage.get("id").getAsString()) { case "start": start(session, jsonMessage); break; case "show_mouths": setVisualization(session, jsonMessage); break; case "scale_factor": log.debug("Case scale factor"); setScaleFactor(session, jsonMessage); break; case "process_num_frames": log.debug("Case process num frames"); setProcessNumberFrames(session, jsonMessage); break; case "width_to_process": log.debug("Case width to process"); setWidthToProcess(session, jsonMessage); break; case "stop": { UserSession user = users.remove(session.getId()); if (user != null) { user.release(); } break; } case "onIceCandidate": { JsonObject candidate = jsonMessage.get("candidate").getAsJsonObject(); UserSession user = users.get(session.getId()); if (user != null) { IceCandidate cand = new IceCandidate( candidate.get("candidate").getAsString(), candidate.get("sdpMid").getAsString(), candidate.get("sdpMLineIndex").getAsInt()); user.addCandidate(cand); } break; } default: sendError(session, "Invalid message with id " + jsonMessage.get("id").getAsString()); break; } }
@Test public void registerWebSocketHandlerWithSockJS() throws Exception { WebSocketSession session = this.webSocketClient .doHandshake(new AbstractWebSocketHandler() {}, getWsBaseUrl() + "/sockjs/websocket") .get(); TestWebSocketHandler serverHandler = this.wac.getBean(TestWebSocketHandler.class); assertTrue(serverHandler.connectLatch.await(2, TimeUnit.SECONDS)); session.close(); }
@Test public void fallbackAfterTransportFailure() throws Exception { this.testFilter.sendErrorMap.put("/websocket", 200); this.testFilter.sendErrorMap.put("/xhr_streaming", 500); TestClientHandler handler = new TestClientHandler(); initSockJsClient(createWebSocketTransport(), createXhrTransport()); WebSocketSession session = this.sockJsClient.doHandshake(handler, this.baseUrl + "/echo").get(); assertEquals("Fallback didn't occur", XhrClientSockJsSession.class, session.getClass()); TextMessage message = new TextMessage("message1"); session.sendMessage(message); handler.awaitMessage(message, 5000); }
@Test public void sendMessageToController() throws Exception { TextMessage message = create(StompCommand.SEND).headers("destination:/app/simple").build(); WebSocketSession session = doHandshake(new TestClientWebSocketHandler(0, message), "/ws").get(); SimpleController controller = this.wac.getBean(SimpleController.class); try { assertTrue(controller.latch.await(10, TimeUnit.SECONDS)); } finally { session.close(); } }
private void sendMessage(final WebSocketSession session, final TextMessage textMessage) { this.taskExecutor.execute( () -> { if (session.isOpen()) { try { session.sendMessage(textMessage); } catch (IOException e) { logger.error("sendMessage to session", e); } } }); }
private <P, R> Response<R> sendRequestWebSocket(Request<P> request, Class<R> resultClass) { log.info("Req-> {}", request.toString()); Future<Response<JsonElement>> responseFuture = null; if (request.getId() != null) { responseFuture = pendingRequests.prepareResponse(request.getId()); } try { synchronized (wsSession) { wsSession.sendMessage(new TextMessage(JsonUtils.toJson(request))); } } catch (Exception e) { throw new KurentoException( "Exception while sending message '" + JsonUtils.toJson(request) + "' to websocket with native sessionId '" + wsSession.getId() + "'", e); } if (responseFuture == null) { return null; } Response<JsonElement> responseJsonObject; try { responseJsonObject = responseFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); log.info("<-Res {}", responseJsonObject.toString()); } catch (InterruptedException e) { // TODO What to do in this case? throw new JsonRpcException("Interrupted while waiting for a response", e); } catch (ExecutionException e) { // TODO Is there a better way to handle this? throw new JsonRpcException("This exception shouldn't be thrown", e); } catch (TimeoutException e) { throw new TransportException( "Timeout of " + TIMEOUT + " milliseconds waiting from response to request with id:" + request.getId(), e); } return MessageUtils.convertResponse(responseJsonObject, resultClass); }
private void start(final WebSocketSession session, JsonObject jsonMessage) { try { // Media Logic (Media Pipeline and Elements) UserSession user = new UserSession(); MediaPipeline pipeline = kurento.createMediaPipeline(); user.setMediaPipeline(pipeline); WebRtcEndpoint webRtcEndpoint = new WebRtcEndpoint.Builder(pipeline).build(); user.setWebRtcEndpoint(webRtcEndpoint); users.put(session.getId(), user); webRtcEndpoint.addOnIceCandidateListener( new EventListener<OnIceCandidateEvent>() { @Override public void onEvent(OnIceCandidateEvent event) { JsonObject response = new JsonObject(); response.addProperty("id", "iceCandidate"); response.add("candidate", JsonUtils.toJsonObject(event.getCandidate())); try { synchronized (session) { session.sendMessage(new TextMessage(response.toString())); } } catch (IOException e) { log.debug(e.getMessage()); } } }); mouth = new NuboMouthDetector.Builder(pipeline).build(); webRtcEndpoint.connect(mouth); mouth.connect(webRtcEndpoint); // SDP negotiation (offer and answer) String sdpOffer = jsonMessage.get("sdpOffer").getAsString(); String sdpAnswer = webRtcEndpoint.processOffer(sdpOffer); // Sending response back to client JsonObject response = new JsonObject(); response.addProperty("id", "startResponse"); response.addProperty("sdpAnswer", sdpAnswer); synchronized (session) { session.sendMessage(new TextMessage(response.toString())); } webRtcEndpoint.gatherCandidates(); } catch (Throwable t) { sendError(session, t.getMessage()); } }
/** 关闭连接后 */ public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { System.out.println("Websocket:" + session.getId() + "已经关闭"); Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator(); // 移除Socket会话 while (it.hasNext()) { Entry<Long, WebSocketSession> entry = it.next(); if (entry.getValue().getId().equals(session.getId())) { userSocketSessionMap.remove(entry.getKey()); System.out.println("Socket会话已经移除:用户ID" + entry.getKey()); break; } } }
/** 消息传输错误处理 */ public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { if (session.isOpen()) { session.close(); } Iterator<Entry<Long, WebSocketSession>> it = userSocketSessionMap.entrySet().iterator(); // 移除Socket会话 while (it.hasNext()) { Entry<Long, WebSocketSession> entry = it.next(); if (entry.getValue().getId().equals(session.getId())) { userSocketSessionMap.remove(entry.getKey()); System.out.println("Socket会话已经移除:用户ID" + entry.getKey()); break; } } }
@Override public void destroy() throws Exception { try { // Notify sessions to stop flushing messages for (WebSocketSession session : this.sessions.values()) { try { session.close(CloseStatus.GOING_AWAY); } catch (Exception e) { logger.error("Failed to close session id '" + session.getId() + "': " + e.getMessage()); } } } finally { this.sessions.clear(); } }
@Test(timeout = 5000) public void fallbackAfterConnectTimeout() throws Exception { TestClientHandler clientHandler = new TestClientHandler(); this.testFilter.sleepDelayMap.put("/xhr_streaming", 10000L); this.testFilter.sendErrorMap.put("/xhr_streaming", 503); initSockJsClient(createXhrTransport()); this.sockJsClient.setConnectTimeoutScheduler(this.wac.getBean(ThreadPoolTaskScheduler.class)); WebSocketSession clientSession = sockJsClient.doHandshake(clientHandler, this.baseUrl + "/echo").get(); assertEquals("Fallback didn't occur", XhrClientSockJsSession.class, clientSession.getClass()); TextMessage message = new TextMessage("message1"); clientSession.sendMessage(message); clientHandler.awaitMessage(message, 5000); clientSession.close(); }
public WebSocketServerSession( String sessionId, Object registerInfo, SessionsManager sessionsManager, WebSocketSession wsSession) { super(sessionId, registerInfo, sessionsManager, wsSession.getId()); this.wsSession = wsSession; this.setRsHelper( new JsonRpcRequestSenderHelper(sessionId) { @Override public <P, R> Response<R> internalSendRequest(Request<P> request, Class<R> resultClass) throws IOException { return sendRequestWebSocket(request, resultClass); } @Override protected void internalSendRequest( Request<? extends Object> request, Class<JsonElement> resultClass, Continuation<Response<JsonElement>> continuation) { sendRequestWebSocket(request, resultClass, continuation); } }); }
private synchronized void sendMessage(WebSocketSession session, TextMessage message) { try { session.sendMessage(message); } catch (IOException e) { log.error("Exception sending message", e); } }
/** 连接成功 */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // TODO Auto-generated method stub System.out.println(session); Map<String, String> select = new HashMap<String, String>(); String userId = (String) session.getAttributes().get("userId"); String userPwd = (String) session.getAttributes().get("userPwd"); select.put("userId", userId); select.put("userPwd", userPwd); String result = webSocketService.getNewMessage(select); session.sendMessage(new TextMessage(result.toString())); WebSocketCache.putMemberSession(select.get("userId"), session); }
public static void manageIfException( WebSocketSession session, String methodName, ExceptionMethodWrapper method) { try { method.exec(); } catch (Throwable t) { log.error(t.getMessage(), t); InternalErrorCodes iec; if (t instanceof AppException) { AppException ae = (AppException) t; iec = ae.getErrorCode(); } else { iec = InternalErrorCodes.UNHANDLED_ERROR; } Response resp = new Response.ResponseBuilder<>(methodName) .status(iec.getStatus().intValue()) .error(new AppError(iec.getMessage(), t.getMessage(), iec.getCode())) .build(); try { session.sendMessage(new TextMessage(resp.toJsonStr())); } catch (Exception e) { log.error(e.getMessage(), e); } } }
protected final SubProtocolHandler findProtocolHandler(WebSocketSession session) { String protocol = null; try { protocol = session.getAcceptedProtocol(); } catch (Exception ex) { // Shouldn't happen logger.error( "Failed to obtain session.getAcceptedProtocol(). Will use the " + "default protocol handler (if configured).", ex); } SubProtocolHandler handler; if (!StringUtils.isEmpty(protocol)) { handler = this.protocolHandlerLookup.get(protocol); Assert.state( handler != null, "No handler for '" + protocol + "' among " + this.protocolHandlerLookup); } else { if (this.defaultProtocolHandler != null) { handler = this.defaultProtocolHandler; } else if (this.protocolHandlers.size() == 1) { handler = this.protocolHandlers.iterator().next(); } else { throw new IllegalStateException( "Multiple protocol handlers configured and " + "no protocol was negotiated. Consider configuring a default SubProtocolHandler."); } } return handler; }
@Override public void closeNativeSession(String reason) { try { wsSession.close(new CloseStatus(CloseStatus.NORMAL.getCode(), reason)); } catch (IOException e) { log.warn("Exception closing webSocket session", e); } }
@Test public void sendMessageToControllerAndReceiveReplyViaTopic() throws Exception { TextMessage message1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", "destination:/topic/increment").build(); TextMessage message2 = create(StompCommand.SEND).headers("destination:/app/increment").body("5").build(); TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, message1, message2); WebSocketSession session = doHandshake(clientHandler, "/ws").get(); try { assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS)); } finally { session.close(); } }
@Test public void sendSubscribeToControllerAndReceiveReply() throws Exception { String destHeader = "destination:/app/number"; TextMessage message = create(StompCommand.SUBSCRIBE).headers("id:subs1", destHeader).build(); TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, message); WebSocketSession session = doHandshake(clientHandler, "/ws").get(); try { assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS)); String payload = clientHandler.actual.get(0).getPayload(); assertTrue( "Expected STOMP destination=/app/number, got " + payload, payload.contains(destHeader)); assertTrue("Expected STOMP Payload=42, got " + payload, payload.contains("42")); } finally { session.close(); } }