websocket 下一代即时通讯的技术前沿.
java 方面的规范jsr 356 已经正式通过,最近着重研究此项技术,尝试在项目中使用websocket技术.
(一)先看下 Java Websocket API
以下是简要的api class diagram
解释一下规范中的基本名词:
Endpoint: 端点, websocke一端的连接点,是一个java组件.
Connection: 连接, 保持websocket 两端间的连接对象.
Session: 一次回话, websocket 建立链接后建立的回话.
ClentEndPoint/ ServerEndPoint : websocket客户端/服务端.
客户端只能连接一个EndPoint.服务端可以链接多个EndPoint.当链接建立后,客户端及服务端均可以主动发送/接受消息,此时,则无客户端及服务器端的概念.
(二) Websocket 服务建立此处只记录在web server 环境下
(1) Websocket 在web server启动时,扫描web-inf/classes及 web-inf/lib下的类,扫描到带有websocket 注解( @serverEndpoint )及Endpoint的子类以及 ServerApplicationConfig子类.ServerApplicationConfig类是为了创建Endpoint子类在系统中的配置. 传入扫描到的Endpoint类, 创建ServerEndPointConfig.class 配置实例, 返回 包含serverEndPoint 的Set集. 此处可创建 而serverEndPoint可 endPoint的URI,timeout等配置,serverEndpoint中包含的Configurator可以预处理webSocket握手handshake,添加特殊的处理过程.(用modifyHandshake实现,其还包含其他有用方法).
(2) 当没有扫描到ServerAppliacationConfig子类的时候,则默认强制为扫描到的annotation类生成ServerEndPointConfig实例,也就是说可以为注解类生成websocket服务配置,及注解类是很容易部署的.
Websocket deploy sequence diagram
ServerApplicationConfig Example:
- public class ExamplesConfig implements ServerApplicationConfig {
- @Override
- public Set<ServerEndpointConfig> getEndpointConfigs(
- Set<Class<? extends Endpoint>> scanned) {
- Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();
- if (scanned.contains(EchoEndpoint.class)) {
- result.add(ServerEndpointConfig.Builder.create( EchoEndpoint.class,
- "/websocket/echoProgrammatic").build());
- } return result;
- }
- @Override
- public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
- // Deploy all WebSocket endpoints defined by annotations in the examples
- // web application. Filter out all others to avoid issues when running
- // tests on Gump
- Set<Class<?>> results = new HashSet<>();
- for (Class<?> clazz : scanned) {
- ServerEndpoint serverEndpoint =clazz.getAnnotation(ServerEndpoint.class);
- ClientEndpoint clientEndpoint = clazz.getAnnotation(ClientEndpoint.class);
- if(serverEndpoint != null || clientEndpoint != null){
- results.add(clazz);
- }
- } return results;
- }
- }
(3) Bulid connection between client and server.
1. 握手客服端发送http升级协议的握手请求,请求服务端建立websocket链接.
此时可以改写服务端的ServerEndpointConfig.Configurator类的modifyHandshake方法,增加握手处理.
2. 服务端接收到握手请求后,判断是否与该客户端建立链接,若同意,则返回同意升级协议,建立websocket链接的响应.否则发送不与升级的响应
3. 客户端收到服务器响应,判断是否升级协议,若升级,则发送建立websocket connection,服务端根据请求url调用响应的Endpoint的onopen方法.
4. 服务端在onopen方法里初始化本次链接中的session信息,包括初始化MessagerHandler,(若是注解,则不需要,注解调用OnMessager注解的方法)
每一种消息类型,服务端只能为session注册唯一的一种类型的MessageHandler.如String类型的数据,只能注册一种处理String类型的MessagerHandler处理.而messagerHandler有两种类型,一种Partial,一种为Whole,
顾名思义,Partial信息,适用于部分消息,当onmessager的last(是否为本次消息的最后一部分消息)参数为true时,与Whole相同;
Whole适用于全部消息.
此处仍有疑问,这个Partial与Whole的适用场景是什么呢?是否可以为同一个session注册消息类型相同但处理类型分别是Partial和Whole的消息处理类呢.
而tomcat的example中, whole用做Endpoint的私有属性,而Partial是Session的局部变量.这又有何分别呢?
当发送响应时,调用session的getRemoteEndPoint方法,RemoteEndPoint也有两种.一种Basic及Async.
Basic类型发送消息为block-io, Async 则为NIO,及一个同步,一个是异步发送.
5. 客服端发送信息,服务器端调用响应的Endpoint的onMessage注解方法或MessagerHandler处理.根据消息类型进行判断具体调用那个方法.
OnMessage有个参数Msg, 可以覆盖该方法,msg的类型不同.但同一种类型只能覆盖一次.如果没有注册,则表示不处理此类型的消息.
6. 保持链接,发送客户端发送ping信息,服务端响应pong message.
7. 当发生服务端发送错误/运行异常时,调用Endpoint 的onError或@OnError注解方法
8. 关闭连接必须调用 onclose方法.以通知客户端关闭链接.
Websocket message exchange diagram: