websocket实战(3) 错误处理及

发布时间:2019-09-05 07:04:38编辑:auto阅读(1942)

    回顾

    websocket实战(1) 入门

    websocket实战(2) 信息处理发送、接收和编码

    通过前面说明,已经轻松构建一个简单的websocket ServerEndPoint了。可以为EndPoint加上×××,编码器,为EndPoint提供支持。但是,作为一个服务器,遇到错误怎么办?websocket作为一个简单的容器组件,也具备简单配置管理功能。

    1.错误处理

    1.1@onError

    其实很简单,就是在ServerEndPoint类中,添加一个方法,要求该方法被@onError修饰。

    如下

    @ServerEndpoint("/testendpoint")
    public class TestEndpoint {
       ...
       @OnError
       public void error(Session session, Throwable t) {
          t.printStackTrace();
          ...
       }
    }


    代码片段(代码没有意义,纯属测试)

    @ServerEndpoint("/echo")
    public class EchoEndpoint {
        @OnMessage
        public String onMessage(String message,Session session) {
            System.out.println("Received : " + message);
            int random = new Random().nextInt(5);
            if(random==3){
                throw new RuntimeException("自定义异常");
            }else{
                System.out.println("random="+random);
            }
            return message+"-"+session.getId();
        }
    
        @OnOpen
        public void myOnOpen(Session session) {
            session.getUserProperties().put("startTime",new Date());
            System.out.println("WebSocket opened: " + session.getId());
        }
    
        @OnClose
        public void myOnClose(CloseReason reason) {
            System.out.println("Closing a WebSocket due to " + reason.getReasonPhrase());
        }
    
        @OnError
        public void error(Session session, Throwable t) {
            System.out.println("发生错误,请注意");
            t.printStackTrace();
        }
    }

    如果遇到错误,将抛异常如下

    发生错误,请注意(error方法中输出)

    java.lang.RuntimeException: 自定义异常
        at com.sample.websocket.endpoint.EchoEndpoint.onMessage(EchoEndpoint.java:21)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeBase.onMessage(PojoMessageHandlerWholeBase.java:80)
        at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:393)
        at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:494)
        at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:289)
        at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
    Closing a WebSocket due to An unrecoverable IOException occurred so the connection was closed(myOnClose调用输出)
        at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:56)
      ....

    1.2 Error Handing

    主要错误有3种。

    Deployment Errors (部署期间,比如tomcat启动)
    Errors Originating in Websocket Application Code(websocket endpoint 发生错误)
    Errors Originating in the Container and/or Underlying Connection(连接peer内部错误)
    javax.websocket. CloseReason

    CloseReason.CloseCode

    public enum CloseCodes implements CloseReason.CloseCode {
    ...
    /**
     * 1006 is a reserved value and MUST NOT be set as a status code in a
     * Close control frame by an endpoint.  It is designated for use in
     * applications expecting a status code to indicate that the
     * connection was closed abnormally, e.g., without sending or
     * receiving a Close control frame.
     */
    CLOSED_ABNORMALLY(1006),
    ...
    }
    //1006是一个比较特殊的错误码
    //其他错误码,参照Enum CloseCodes

    if the local
    container determines the session has timed out, the local implementation must use the websocket protocol
    close code
    1006

    2.配置管理

    2.1 先从javax.websocket.Session说起

    Session 代表一个有效连接。周期从(Open ->Close)

    wKioL1fmRSPzbyNgAACOZi444dY166.png

    简单将Session的方法分为几类(当然你也许有更好的分类)

    1. 配置信息(其中有些配置信息来源于WebContainer)

    2. Session状态信息

    3. 自定义存储信息

    4. 请求信息

    5. Session的操作方法

    测试代码

    package com.sample.websocket.endpoint;
    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.nio.ByteBuffer;
    import java.util.Iterator;
    import java.util.Set;
    
    @ServerEndpoint("/config")
    public class ReceiveEndpoint {
       @OnMessage
       public void textMessage(Session session, String msg) {
          System.out.println("Text message: " + msg);
          Set<Session> openSessions = session.getOpenSessions();
          System.out.println(" session.OpenSessions.size" + openSessions.size());
       }
       @OnMessage
       public void binaryMessage(Session session, ByteBuffer msg) {
          System.out.println("Binary message: " + msg.toString());
       }
       @OnMessage
       public void pongMessage(Session session, PongMessage msg) {
          System.out.println("Pong message: " +  msg.getApplicationData().toString());
       }
    
       @OnClose
       public void nClose(Session session, CloseReason reason) {
          System.out.println("Pong message: " +  reason.getReasonPhrase()+"-->"+reason.getCloseCode());
       }
    
       @OnError
       public void onError(Throwable t){
          t.printStackTrace();
       }
       @OnOpen
       public void onOpen(Session session){
          System.out.println("session.getId()=" + session.getId());
    
          Set<Session> openSessions = session.getOpenSessions();
          System.out.println(" session.OpenSessions.size=" + openSessions.size());
          session.getUserProperties().put("userName","Guest"+session.getId());
    
          Iterator<Session> iterator = openSessions.iterator();
          System.out.println("open session list===========");
          while(iterator.hasNext()){
             Session s = iterator.next();
             String sid = s.getId();
             System.out.println("session.id==>"+ sid);
             Object user = s.getUserProperties().get("userName");
             System.out.println("session."+sid+"username==>"+user);
          }
          System.out.println();
          System.out.println();
    
          WebSocketContainer wsc = session.getContainer();
          System.out.println("WebSocketContainer Info==========");
          System.out.println("webSocketContainer=" + wsc);
          System.out.println("defaultAsyncTimeout->"+wsc.getDefaultAsyncSendTimeout());
          System.out.println("defaultMaxBinaryMessageBufferSize-->"+wsc.getDefaultMaxBinaryMessageBufferSize());
          System.out.println("defaultMaxSessionIdleTimeout-->"+wsc.getDefaultMaxSessionIdleTimeout());
          System.out.println("installedExtensions.size==>" + wsc.getInstalledExtensions().size());
          System.out.println();
          System.out.println();
          System.out.println("session parameter");
          System.out.println("session.getMaxBinaryMessageBufferSize()==>"+session.getMaxBinaryMessageBufferSize());
          System.out.println("session.getMaxIdleTimeout()==>"+session.getMaxIdleTimeout());
          System.out.println("session.getNegotiatedSubprotocol()==>"+session.getNegotiatedSubprotocol());
          System.out.println("session.getProtocolVersion()==>"+session.getProtocolVersion());
          System.out.println("session.getRequestURI()==>" + session.getRequestURI());
          System.out.println();
          System.out.println();
          Set<MessageHandler> messageHandlers = session.getMessageHandlers();
          Iterator<MessageHandler> messageHandlerIterator = messageHandlers.iterator();
          System.out.println("MessageHandlers");
          while(messageHandlerIterator.hasNext()){
             MessageHandler handler = messageHandlerIterator.next();
             System.out.println(handler.toString());
          }
          System.out.println("session.getUserPrincipal().getName()==>"+session.getUserPrincipal());
       }
    }

    输出结果如下

    session.getId()=0
     session.OpenSessions.size=0
    open session list===========

    WebSocketContainer Info==========
    webSocketContainer=org.apache.tomcat.websocket.server.WsServerContainer@4d3de8ab
    defaultAsyncTimeout->-1
    defaultMaxBinaryMessageBufferSize-->8192
    defaultMaxSessionIdleTimeout-->0
    installedExtensions.size==>0


    session parameter
    session.getMaxBinaryMessageBufferSize()==>8192
    session.getMaxIdleTimeout()==>0
    session.getNegotiatedSubprotocol()==>
    session.getProtocolVersion()==>13
    session.getRequestURI()==>/wsexample/config

    MessageHandlers
    org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholePong@6e710882
    org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeBinary@3ee5c773
    org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeText@1f69b445
    session.getUserPrincipal().getName()==>null
    Text message: Hello WebSocket!
     session.OpenSessions.size=1


    下面是我分析结果

    1.session.id是从0递增的

    2.session.getOpenSessions(),API定义是获取连接EndPoint的Session的数目,但实际上永远返回1

    3.session.getOpenSessions() 在@OnOpen方法中调用返回0,在@OnMessage方法中调用返回1,即便打开了多个连接也是如此。在Tomcat7.72中如此。

    4.定义了3个OnMessage修饰的方法,MessageHandler也是三个

    5.几个参数从WebSocketContainer中的参数,与Session的参数一致。比如MaxBinaryMessageBufferSize等

    6.即使在方法中修改了WebSocketContainer的配置参数值,session中对应的值也不会变。

    session.getOpenSessions() API

    Return a copy of the Set of all the open web socket sessions that represent connections to the same endpoint to which this session represents a connection. The Set includes the session this method is called on.

    调整Session中的若干参数值

        @OnOpen 
        public void onOpen(final Session session) { 
            session.setMaxIdleTimeout(TIMEOUT); 
            ... 
        }

关键字