当用户重新加载网页和websocket会话超时时,如何在服务器端正确关闭websocket会话?

时间:2014-12-18 17:49:21

标签: java websocket tomcat8

用户重新加载页面时

java.lang.IllegalStateException。调用onClose方法我得到了套接字已经关闭的异常。因此,我不能清理在Web套接字会话关闭时停止的不同Thread中的一些东西。如果用户在没有重新加载页面的情况下关闭会话(它基本上是websocket上的服务器端终端),一切都很好。我在tomcat8上。

问题。

  1. 如果用户重新加载页面,如何正确关闭服务器上的websocket会话?
  2. 当webshocket会话超时时,我该如何做一些事情呢?
  3. 以下是为websocket连接提供服务的代码。

    @ServerEndpoint(value = "/ws/{clientId}", configurator = HttpSessionConfigurator.class)
    public class WSSession {
        public static final long DEFAULT_TIMEOUT = 3600000;
        public static final int DEFAULT_TIMEOUT_IN_SECONDS = 3600;
    
        private Session wsSession;
        private HttpSession httpSession;
    
        private static final ConfigService configService;
    
        static {
            configService = ConfigServiceFactory.getConfigService(System
                    .getenv("ENVRIRONMENT"));
        }
    
        @OnOpen
        public void onOpen(@PathParam("clientId") String clientId, Session session,
                EndpointConfig config) throws IOException {
            Logger logger = Logger.getLogger(clientId);
            try {
                FileHandler fh = new FileHandler(configService.logDirectory()
                        .toString() + "/" + clientId);
                SimpleFormatter formatter = new SimpleFormatter();
                fh.setFormatter(formatter);
                logger.addHandler(fh);
                logger.setLevel(Level.INFO);
            } catch (SecurityException | IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            System.out.println("Got ID: " + clientId);
            // TODO: Get the max timeout from configuration
            session.setMaxIdleTimeout(DEFAULT_TIMEOUT);
            ConnectionDetails cd = configService.getConnectionDetails(clientId);
            this.wsSession = session;
            this.httpSession = (HttpSession) config.getUserProperties().get(
                    HttpSession.class.getName());
            if (this.httpSession.getAttribute(SessionProperties.WS_SESSION.value()) != null
                    && this.httpSession.getAttribute(SessionProperties.WS_SESSION
                            .value()) != session) {
                Session wSession = (Session) this.httpSession
                        .getAttribute(SessionProperties.WS_SESSION.value());
                if (wSession.isOpen()) {
                    wSession.close();
                }
            }
            // TODO: Code changes for password based login
            // For now there is no password login.
            // TODO: For now the passphrase is NULL and strict checking is disabled
            this.wsSession = session;
            try {
                Connection connection = new Connection(cd.getHost(), cd.getPort());
                connection.connect();
                boolean authenticated = connection.authenticateWithPublicKey(
                        cd.getUserName(), cd.getPrivateKey().toCharArray(), null);
                if (authenticated) {
                    ch.ethz.ssh2.Session sshSession = connection.openSession();
                    sshSession.requestPTY("xterm", 120, 40, 14, 14, null);
                    OutputStream out = sshSession.getStdin();
                    InputStream in = new StreamGobbler(sshSession.getStdout());
                    sshSession.startShell();
                    Basic basicRemote = session.getBasicRemote();
                    Async asyncRemote = session.getAsyncRemote();
                    httpSession.setAttribute(SessionProperties.CLIENT_ID.value(),
                            clientId);
                    httpSession.setAttribute(SessionProperties.SSH_SESSION.value(),
                            sshSession);
                    httpSession.setAttribute(SessionProperties.IN_SSH.value(), out);
                    httpSession.setAttribute(SessionProperties.OUT_SSH.value(), in);
                    httpSession.setAttribute(
                            SessionProperties.BASIC_REMOTE.value(), basicRemote);
                    httpSession.setAttribute(SessionProperties.WS_SESSION.value(),
                            wsSession);
                    httpSession.setAttribute(
                            SessionProperties.ASYNC_REMOTE.value(), asyncRemote);
                    Thread worker = new Thread(new OutputTask(httpSession,
                            wsSession));
                    worker.start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @OnMessage
        public void onMessage(String message, Session session) throws IOException {
            // IN_SSH is connection from this machine to remote machine.
            // OUT_SSH is connection from this remote machine to this machine.
            byte[] bytes = message.getBytes();
            OutputStream out = (OutputStream) httpSession
                    .getAttribute(SessionProperties.IN_SSH.value());
            out.write(bytes);
        }
    
        @OnClose
        public void onClose(Session session) throws IOException {
            if (session.isOpen()) {
                try {
                    session.close();
                } catch (IllegalStateException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    

    我有一个在socket打开的情况下运行的线程,但是当用户重新加载时,我不会在输出中看到Exiting OutputTask - 它会在正常的套接字关闭时打印出来。该线程的代码。

    @Override
    public void run() {
        InputStream stream = (StreamGobbler) session
                .getAttribute(SessionProperties.OUT_SSH.value());
        Basic basicRemote = (Basic) session
                .getAttribute(SessionProperties.BASIC_REMOTE.value());
        int read = 0;
        byte[] bt = new byte[1024];
        try {
            while (wsSession.isOpen() && (read = stream.read(bt)) != -1) {
                logger.info(new String(bt));
                basicRemote.sendText(new String(Arrays.copyOf(bt, read)));
                // TODO: Add logging and finalize code
            }
        } catch (SecurityException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                Handler[] handlers = logger.getHandlers();
                for (Handler h : handlers) {
                    h.close();
                }
                logger = null;
                if (wsSession.isOpen()) {
                    wsSession.close();
                }
                if (stream != null) {
                    stream.close();
                }
                System.out.println("Exiting OutputTask");
                session.invalidate();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    

    用户重新加载页面时出错。

    java.lang.IllegalStateException: Message will not be sent because the WebSocket session has been closed
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:378)
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:335)
        at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:264)
        at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:536)
        at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:464)
        at org.apache.tomcat.websocket.WsSession.close(WsSession.java:441)
        at org.apache.tomcat.websocket.WsSession.close(WsSession.java:435)
        at com.example.websocket.wss.onClose(WSSession.java:130)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at org.apache.tomcat.websocket.pojo.PojoEndpointBase.onClose(PojoEndpointBase.java:107)
        at org.apache.tomcat.websocket.WsSession.fireEndpointOnClose(WsSession.java:508)
        at org.apache.tomcat.websocket.WsSession.onClose(WsSession.java:491)
        at org.apache.tomcat.websocket.WsFrameBase.processDataControl(WsFrameBase.java:342)
        at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:284)
        at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
        at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
        at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:203)
        at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:194)
        at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:95)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:653)
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
    

0 个答案:

没有答案