﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-我的博客-随笔分类-WebSocket</title><link>http://www.blogjava.net/qbna350816/category/55091.html</link><description>201103</description><language>zh-cn</language><lastBuildDate>Sat, 23 Jul 2016 17:39:01 GMT</lastBuildDate><pubDate>Sat, 23 Jul 2016 17:39:01 GMT</pubDate><ttl>60</ttl><item><title>JSR 356- WebSocket Java API </title><link>http://www.blogjava.net/qbna350816/archive/2016/07/24/431302.html</link><dc:creator>胡小军</dc:creator><author>胡小军</author><pubDate>Sat, 23 Jul 2016 17:35:00 GMT</pubDate><guid>http://www.blogjava.net/qbna350816/archive/2016/07/24/431302.html</guid><wfw:comment>http://www.blogjava.net/qbna350816/comments/431302.html</wfw:comment><comments>http://www.blogjava.net/qbna350816/archive/2016/07/24/431302.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qbna350816/comments/commentRss/431302.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qbna350816/services/trackbacks/431302.html</trackback:ping><description><![CDATA[<h1><strong style="line-height: 1.5; font-size: 14px;">原文：<a href="http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html" style="cursor: pointer;">http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html</a><br /></strong></h1><h1><strong style="line-height: 1.5;">学习如何在你的应用程序中集成WebSockets.</strong></h1><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">Published April 2013</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">对于许多基于客户端－服务器程序来说，老的HTTP 请求-响应模型已经有它的局限性. 信息必须通过多次请求才能将其从服务端传送到客户端.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">过去许多的黑客使用某些技术来绕过这个问题，例如：长轮询(long polling)、<span style="color: #333333;">基于 HTTP 长连接的服务器推技术(<span style="color: #000000;">Comet)</span></span>.&nbsp;</p><div style="font-family: 微软雅黑; background-color: #ffffff;">然而，基于标准的、双向的、客户端和服务器之间全双工的信道需求再不断增加。</div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">在2011年, IETF发布了标准WebSocket协议－RFC 6455. 从那时起，大多数Web浏览器都实现了支持WebSocket协议的客户端APIs.同时，许多Java 包也开始实现了WebSocket协议.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><span style="line-height: 1.5; background-color: inherit;">WebSocket协议利用HTTP升级技术来将HTTP连接升级到WebSocket</span>. 一旦升级后，连接就有了在两个方向上相互独立(全双式)发送消息(数据桢)的能力.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">不需要headers 或cookies,<span style="line-height: 1.5; background-color: inherit;">这大大降低了所需的带宽</span><span style="font-family: 'Microsoft Yahei'; line-height: 1.5;">.&nbsp;</span><span style="line-height: 1.5; background-color: inherit;">通常，WebSockets来周期性地发送小消息</span><span style="font-family: 'Microsoft Yahei'; line-height: 1.5;">&nbsp;(例如，几个字节).&nbsp;</span></p><div style="font-family: 微软雅黑; background-color: #ffffff;">额外的<span style="font-family: 'Microsoft Yahei';">headers</span>常常会使开销大于有效负载（<span style="font-family: 'Microsoft Yahei';">payload</span>）。</div><h2><span style="font-size: 14px;">JSR 356</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">JSR 356, WebSocket的Java API, 明确规定了API，当Java开发者需要在应用程序中集成WebSocket时，就可以使用此API&#8212;服务端和客户端均可. 每个声明兼容JSR 356的WebSocket协议，都必须实现这个API.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><span style="line-height: 1.5; background-color: inherit;">因此，开发人员可以自己编写独立于底层WebSocket实现的WebSocket应用。</span><span style="line-height: 1.5; background-color: inherit;">这是一个巨大的好处，因为它可以防止供应商锁定，并允许更多的选择、自由的库、应用程序服务器。</span></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><span style="line-height: 1.5; background-color: inherit;">JSR&nbsp;356是即将到来的java&nbsp;EE&nbsp;7标准的一部分，因此，所有与</span>Java EE 7兼容的应用服务器都有JSR 365标准WebSocket的实现.<span style="line-height: 1.5; background-color: inherit;">一旦建立，WebSocket客户端和服务器节点已经是对称的了。客户端API与服务器端API的区别是很小的，</span><span style="line-height: 1.5; font-family: 'Microsoft Yahei';">JSR 356定义的Java client API只是Java EE7完整API的子集.</span></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">客户段－服务器端程序使用WebSockets，通常会包含一个服务器组件和多个客户端组件, 如图１所示:</p><img src="file:///C:/Users/Administrator/AppData/Local/YNote/data/huiwen_82132000@163.com/af6a4fd276894a4889e42ea55bd64f91/1937159.gif" alt="Figure 1" data-media-type="image" data-attr-org-src-id="36832E168D8F41DC82A3AF8F2C1B9E87" data-attr-org-img-file="file:///C:/Users/Administrator/AppData/Local/YNote/data/huiwen_82132000@163.com/af6a4fd276894a4889e42ea55bd64f91/1937159.gif" style="cursor: default; display: inline-block; margin-top: 8px; max-width: 800px; height: auto !important;" /><div style="font-family: 微软雅黑; background-color: #ffffff;"><img src="http://www.blogjava.net/images/blogjava_net/qbna350816/1937159.gif" width="548" height="374" alt="" /></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><strong style="background-color: inherit;">图1</strong></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">在这个例子中，server application 是通过Java编写的,WebSocket 协议细节是由包含在Java EE 7容器中JSR 356 实现来处理的.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">JavaFX 客户端可依赖任何与JSR 356兼容的客户端实现来处理WebSocket协议问题.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">其它客户端(如,iOS 客户端和HTML5客户端)可使用其它 (非Java)与RFC6455兼容的实现来与server application通信.</p><h2><span style="font-size: 14px;">编程模型</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><span data-aligning="#tran_0,#src_0" style="color: #666666; text-align: justify; background-color: rgba(255, 255, 255, 0.8);">JSR 356定义的专家小组,希望支持Java EE开发人员常用的模式和技术。</span><span data-aligning="#tran_1,#src_1" style="color: #666666; text-align: justify; background-color: rgba(255, 255, 255, 0.8);">因此,JSR 356使用了注释和注入。</span></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">一般来说，支持两种编程模型:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;">注解驱动(annotation-driven). 通过使用注解POJOs, 开发者可与WebSocket生命周期事件交互.</li><li style="background-color: inherit;"><strong style="background-color: inherit;">接口驱动(<span style="font-weight: normal;">interface-driven)</span>. 开发者可实现<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Endpoint接口和与生命周期交互的方法.</code></strong></li></ul><h2><span style="font-size: 14px;">生命周期事件</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">典型的WebSocket 交互生命周期如下:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;">一端 (客户端) 通过发送HTTP握手请求来初始化连接.</li><li style="background-color: inherit;">其它端(服务端) 回复握手响应.</li><li style="background-color: inherit;">建立连接.从现在开始，连接是完全对称的.</li><li style="background-color: inherit;">两端都可发送和接收消息.</li><li style="background-color: inherit;">其中一端关闭连接.</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">大部分WebSocket生命周期事件都与Java方法对应，不管是 annotation-driven 还是interface-driven.</p><h2><span style="font-size: 14px;">Annotation-Driven 方式</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">接受WebSocket请求的端点可以是以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@ServerEndpoint</code>&nbsp;注解的POJO.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">此注解告知容器，此类应该被认为是WebSocket端点.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">必须的<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">value</code>&nbsp;元素指定了WebSocket端点的路径.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">考虑下面的代码片断:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@ServerEndpoint("/hello")  public class MyEndpoint { } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">此代码将会以相对路径hello来发布一个端点.在后续方法调用中，此路径可携带路径参数，如：&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">/hello/{userid}是一个有效路径，在这里</code><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">{userid}</code>&nbsp;的值，可在生命周期方法使用<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@PathParam</code>&nbsp;注解获取.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">在GlassFish中，如果你的应用程序是用上下文<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">mycontextroot</code>&nbsp;部署的，且在<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">localhost的<span style="color: #000000; font-family: 'Microsoft Yahei';">8080端口上监听</span></code>, WebSocket可通过使用<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">ws://localhost:8080/mycontextroot/hello来访问</code>.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">初始化WebSocket连接的端点可以是以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@ClientEndpoint</code>&nbsp;注解的POJO.<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@ClientEndpoint</code>&nbsp;和 @<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">ServerEndpoint的主要区别是</code><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">ClientEndpoint</code>&nbsp;不接受路径路值元素，因为它监听进来的请求。</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@ClientEndpoint  public class MyClientEndpoint {} </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">Java中使用注解驱动POJO方式来初始化WebSocket连接，可通过如下代码来完成:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer();  container.conntectToServer(MyClientEndpoint.class, new URI("ws://localhost:8080/tictactoeserver/endpoint")); </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">此后，以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@ServerEndpoint</code>&nbsp;或<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@ClientEndpoint</code>&nbsp;注解的类都称为注解端点.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">一旦建立了WebSocket连接 ，就会创建&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session，并且会调用注解端点中以</code><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnOpen注解的方法.</code>&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">此方法包含了几个参数:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Session</code>&nbsp;参数, 代表创建的<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session</code></li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">EndpointConfig</code>&nbsp;实例包含了关于端点配置的信息</li><li style="background-color: inherit;">０个或多个以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666;">@PathParam注解的</code>字符串参数,指的是端点路径的path参数</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">下面的方法实现了当打开WebSocket时，将会打印session的标识符:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@OnOpen public void myOnOpen (Session session) {    System.out.println ("WebSocket opened: "+session.getId()); } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session实例只要WebSocket未关闭就会一直有效</code>.&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session类中包含了许多有意思的方法，以允许开发者获取更多关于的信息</code>。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">同时,<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session</code>&nbsp;也包含了应用程序特有的数据钩子,即通过<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">getUserProperties()</code>&nbsp;方法来返回&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Map&lt;String, Object&gt;</code>.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">这允许开发者可以使用session-和需要在多个方法调用间共享的应用程序特定信息来填充<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session</code>实例.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">i当WebSocket端收到消息时，将会调用以<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnMessage</code>注解的方法.以<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnMessage</code>&nbsp;注解的方法可包含下面的参数:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Session</code>&nbsp;参数.</li><li style="background-color: inherit;">０个或多个以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666;">@PathParam注解的</code>字符串参数,指的是端点路径的path参数</li><li style="background-color: inherit;">消息本身. 下面有可能消息类型描述.</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">当其它端发送了文本消息时，下面的代码片断会打印消息内容:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@OnMessage public void myOnMessage (String txt) {    System.out.println ("WebSocket received message: "+txt); }  </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">如果以<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnMessage</code>&nbsp;i注解的方法返回值不是<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">void</code>, WebSocket实现会将返回值发送给其它端点.下面的代码片断会将收到的文本消息以首字母大写的形式发回给发送者:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@OnMessage public String myOnMessage (String txt) {    return txt.toUpperCase(); }  </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">另一种通过WebSocket连接来发送消息的代码如下:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">RemoteEndpoint.Basic other = session.getBasicRemote(); other.sendText ("Hello, world"); </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">在这种方式中，我们从<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Session</code>&nbsp;对象开始，它可以从生命周期回调方法中获取(例如,以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnOpen注解的方法</code>).session实例上<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">getBasicRemote()</code>&nbsp;方法返回的是WebSocket其它部分的代表<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">RemoteEndpoint</code>.&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">RemoteEndpoint</code>&nbsp;实例可用于发送文本或其它类型的消息，后面有描述.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">当关闭WebSocket连接时,将会调用<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnClose</code>&nbsp;注解的方法。此方法接受下面的参数:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Session</code>&nbsp;参数. 注意，一旦WebSocket真正关闭了，此参数就不能被使用了，这通常发生在<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnClose</code>&nbsp;注解方法返回之后.</li><li style="background-color: inherit;">A&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.CloseReason</code>&nbsp;参数，用于描述关闭WebSocket的原因,如：正常关闭，协议错误,<span style="line-height: 1.5;">服务过载等等</span><span style="line-height: 1.5;">.</span></li><li style="background-color: inherit;">０个或多个以&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666;">@PathParam注解的</code>字符串参数,指的是端点路径的path参数</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">下面的代码片段打印了WebSocket关闭的原因:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@OnClose public void myOnClose (CloseReason reason) {    System.out.prinlnt ("Closing a WebSocket due to "+reason.getReasonPhrase()); } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">完整情况下，这里还有一个生命周期注解:如果收到了错误，将会调用&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnError</code>&nbsp;注解的方法。</p><h2><span style="font-size: 14px;">Interface-Driven 方式</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">annotation-driven 方式允许我们注解一个Java类，以及使用生命周期注解来注解方法.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">使用interface-driven方式,开发者可继承<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Endpoint</code>&nbsp;并覆盖其中的<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">onOpen</code>,&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">onClose</code>, 以及<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">onError</code>&nbsp;方法:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">public class myOwnEndpoint extends javax.websocket.Endpoint {    public void onOpen(Session session, EndpointConfig config) {...}    public void onClose(Session session, CloseReason closeReason) {...}    public void onError (Session session, Throwable throwable) {...} } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">为了拦截消息，需要在onOpen实现中注册一个<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.MessageHandler</code>:</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">public void onOpen (Session session, EndpointConfig config) {    session.addMessageHandler (new MessageHandler() {...}); } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler</code>&nbsp;接口有两个子接口:&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler.Partial和</code>&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler.Whole</code>.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler.Partial</code>&nbsp;接口应该用于当开发者想要收到部分消息通知的时候,<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler.Whole的实现应该用于整个消息到达通知</code>。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">下面的代码片断会监听进来的文件消息，并将文本信息转换为大小版本后发回给其它端点：</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">public void onOpen (Session session, EndpointConfig config) {    final RemoteEndpoint.Basic remote = session.getBasicRemote();    session.addMessageHandler (new MessageHandler.Whole&lt;String&gt;() {       public void onMessage(String text) {                  try {                      remote.sendString(text.toUpperCase());                  } catch (IOException ioe) {                      // handle send failure here                  }              }     }); } </pre><div style="font-family: 微软雅黑; background-color: #ffffff;"></div><h2><span style="font-size: 14px;">消息类型，编码器，解码器</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">WebSocket的JavaAPI非常强大，因为它允许发送任或接收任何对象作为WebSocket消息.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><span style="line-height: 1.5; background-color: inherit;">基本上，有三种不同类型的消息</span><span style="font-family: 'Microsoft Yahei'; line-height: 1.5;">:</span></p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;">基于文本的消息</li><li style="background-color: inherit;">二进制消息</li><li style="background-color: inherit;">Pong 消息,它是WebSocket连接自身</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">当使用interface-driven模式,每个session最多只能为这三个不同类型的消息注册一个<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageHandler</code>.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">当使用annotation-driven模式,针对不同类型的消息，只允许出现一个<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@onMessage</code>&nbsp;注解方法. 在注解方法中，消息内容中允许的参数依赖于消息类型。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/OnMessage.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;">Javadoc for the&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">@OnMessage</code>&nbsp;annotation</a>&nbsp;明确指定了消息类型上允许出现的消息参数:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;">"如果方法用于处理文本消息:&nbsp;<br style="background-color: inherit;" /><br style="background-color: inherit;" /><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; background-color: inherit;"><li style="background-color: inherit;"><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/String.html?is-external=true" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">String</code></a>&nbsp;用于接收整个消息</li><li style="background-color: inherit;">Java 原型或等价的类用于接收整个消息并将其转换为此类型</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">String</code>&nbsp;和 boolean 对用于部分接收消息</li><li style="background-color: inherit;"><a href="http://docs.oracle.com/javase/6/docs/api/java/io/Reader.html?is-external=true" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Reader</code></a>&nbsp;用于以阻塞流的方式接收整个消息</li><li style="background-color: inherit;">端点的任何对象参数存在文本解码器 (<a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/Decoder.Text.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.Text</code></a>&nbsp;或&nbsp;<a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/Decoder.TextStream.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.TextStream</code></a>).</li></ul></li><li style="background-color: inherit;">如果方法用于处理二进制消息:&nbsp;<br style="background-color: inherit;" /><br style="background-color: inherit;" /><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; background-color: inherit;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">byte[]</code>&nbsp;或&nbsp;<a href="http://docs.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html?is-external=true" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">ByteBuffer</code></a>&nbsp;用于接收整个消息</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">byte[]</code>&nbsp;和 boolean 对, 或者&nbsp;<a href="http://docs.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html?is-external=true" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">ByteBuffer</code></a>&nbsp;和boolean对用于部分接收消息</li><li style="background-color: inherit;"><a href="http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html?is-external=true" style="cursor: pointer; font-family: 'Microsoft Yahei'; line-height: 1.5; color: #1f4f82;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">InputStream</code></a><span style="font-family: 'Microsoft Yahei'; line-height: 1.5;">&nbsp;用于按阻塞流的方式接收整个消息</span></li><li style="background-color: inherit;">端点的任何对象参数存在二进制解码器(<a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/Decoder.Binary.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.Binary</code></a>&nbsp;or&nbsp;<a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/Decoder.BinaryStream.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.BinaryStream</code></a>).</li></ul></li><li style="background-color: inherit;">如果方法是用于处理pong消息:&nbsp;<br style="background-color: inherit;" /><br style="background-color: inherit;" /><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; background-color: inherit;"><li style="background-color: inherit;"><a href="http://javaee-spec.java.net/nonav/javadocs/javax/websocket/PongMessage.html" style="cursor: pointer; color: #1f4f82; background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">PongMessage</code></a>&nbsp;用于处理pong消息"</li></ul></li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">任何Java对象使用编码器都可以编码为基于文本或二进制的消息.这种基于文本或二进制的消息将转输到其它端点，在其它端点，它可以解码成Java对象－或者被另外的WebSocket 包解释.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">通常情况下，XML或JSON用于来传送WebSocket消息, 编码/解码然后会将Java对象编组成XML或JSON并在另一端解码为Java对象.</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">encoder是以<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Encoder</code>&nbsp;接口的实现来定义,decoder是以<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">javax.websocket.Decoder</code>&nbsp;接口的实现来定义的.&nbsp;</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">有时，端点实例必须知道encoders和decoders是什么.使用annotation-driven方式, 可向<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666;">@ClientEndpoint</code>&nbsp;和&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666;">@ServerEndpoint</code>&nbsp;l注解中的encode和decoder元素传递 encoders和decoders的列表。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">Listing 1 中的代码展示了如何注册一个&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageEncoder</code>&nbsp;类(它定义了MyJavaObject实例到文本消息的转换).&nbsp;<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">MessageDecoder</code>&nbsp;是以相反的转换来注册的.</p><pre style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; background-color: #ffffff;">@ServerEndpoint(value="/endpoint", encoders = MessageEncoder.class, decoders= MessageDecoder.class) public class MyEndpoint { ... }  class MessageEncoder implements Encoder.Text&lt;MyJavaObject&gt; {    @override    public String encode(MyJavaObject obj) throws EncodingException {       ...    } }  class MessageDecoder implements Decoder.Text&lt;MyJavaObject&gt; {    @override     public MyJavaObject decode (String src) throws DecodeException {       ...    }     @override     public boolean willDecode (String src) {       // return true if we want to decode this String into a MyJavaObject instance    } } </pre><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><strong style="background-color: inherit;">Listing 1</strong></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Encoder</code>&nbsp;接口有多个子接口:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Encoder.Text</code>&nbsp;用于将Java对象转成文本消息</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Encoder.TextStream</code>&nbsp;用于将Java对象添加到字符流中</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; line-height: 1.5; color: #666666;">Encoder.Binary</code><span style="font-family: 'Microsoft Yahei'; line-height: 1.5;">&nbsp;用于将Java对象转换成二进制消息</span></li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Encoder.BinaryStream</code>&nbsp;用于将Java对象添加到二进制流中</li></ul><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">类似地,<code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder</code>&nbsp;接口有四个子接口:</p><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.Text</code>&nbsp;用于将文本消息转换成Java对象</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.TextStream</code>&nbsp;用于从字符流中读取Java对象</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.Binary</code>&nbsp;用于将二进制消息转换成Java对象</li><li style="background-color: inherit;"><code style="font-family: Monaco, Consolas, Courier, 'Lucida Console', monospace; color: #666666; background-color: inherit;">Decoder.BinaryStream</code>&nbsp;用于从二进制流中读取Java对象</li></ul><h2><span style="font-size: 14px;">结论</span></h2><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; background-color: #ffffff;">WebSocket Java API为Java开发者提供了标准API来集成IETF WebSocket标准.通过这样做，Web 客户端或本地客户端可使用任何WebSocket实现来轻易地与Java后端通信。</p><div style="font-family: 微软雅黑; background-color: #ffffff;">Java Api是高度可配置的，灵活的，它允许java开发者使用他们喜欢的模式。</div><h2><span style="font-size: 14px;">也可参考</span></h2><ul data-front-font-size="14px" style="margin-top: 0px; margin-bottom: 0px; font-family: 微软雅黑; background-color: #ffffff;"><li style="background-color: inherit;"><a href="http://jcp.org/en/jsr/detail?id=356" style="cursor: pointer; color: #1f4f82; background-color: inherit;">JSR 356 on jcp.org</a></li><li style="background-color: inherit;"><a href="http://java.net/projects/websocket-spec" style="cursor: pointer; color: #1f4f82; background-color: inherit;">JSR 356 specification work on java.net</a></li><li style="background-color: inherit;"><a href="http://java.net/projects/tyrus" style="cursor: pointer; color: #1f4f82; background-color: inherit;">Tyrus (JSR 356 Reference Implementation) on java.net</a></li><li style="background-color: inherit;"><a href="http://jcp.org/en/jsr/detail?id=342" style="cursor: pointer; color: #1f4f82; background-color: inherit;">JSR 342, the Java EE 7 umbrella specification on jcp.org</a></li><li style="background-color: inherit;"><a href="http://glassfish.java.net/" style="cursor: pointer; color: #1f4f82; background-color: inherit;">GlassFish, the Reference Implementation for Java EE</a></li></ul><img src ="http://www.blogjava.net/qbna350816/aggbug/431302.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qbna350816/" target="_blank">胡小军</a> 2016-07-24 01:35 <a href="http://www.blogjava.net/qbna350816/archive/2016/07/24/431302.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>