/**
	 * 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();
    }
  }