回顾
通过前面说明,已经轻松构建一个简单的websocket ServerEndPoint了。可以为EndPoint加上解码器,编码器,为EndPoint提供支持。但是,作为一个服务器,遇到错误怎么办?websocket作为一个简单的容器组件,也具备简单配置管理功能。
1.错误处理
[email protected]
其实很简单,就是在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)
简单将Session的方法分为几类(当然你也许有更好的分类)
- 配置信息(其中有些配置信息来源于WebContainer)
- Session状态信息
- 自定义存储信息
- 请求信息
- 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==========
webSocke[email protected]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
[email protected]10882
[email protected]ee5c773
[email protected]9b445
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); ... }