隨筆 - 41  文章 - 7  trackbacks - 0
          <2016年7月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          原文:http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html

          學習如何在你的應用程序中集成WebSockets.

          Published April 2013

          對于許多基于客戶端-服務器程序來說,老的HTTP 請求-響應模型已經有它的局限性. 信息必須通過多次請求才能將其從服務端傳送到客戶端.

          過去許多的黑客使用某些技術來繞過這個問題,例如:長輪詢(long polling)、基于 HTTP 長連接的服務器推技術(Comet)

          然而,基于標準的、雙向的、客戶端和服務器之間全雙工的信道需求再不斷增加。

          在2011年, IETF發布了標準WebSocket協議-RFC 6455. 從那時起,大多數Web瀏覽器都實現了支持WebSocket協議的客戶端APIs.同時,許多Java 包也開始實現了WebSocket協議.

          WebSocket協議利用HTTP升級技術來將HTTP連接升級到WebSocket. 一旦升級后,連接就有了在兩個方向上相互獨立(全雙式)發送消息(數據楨)的能力. 

          不需要headers 或cookies,這大大降低了所需的帶寬通常,WebSockets來周期性地發送小消息 (例如,幾個字節). 

          額外的headers常常會使開銷大于有效負載(payload)。

          JSR 356

          JSR 356, WebSocket的Java API, 明確規定了API,當Java開發者需要在應用程序中集成WebSocket時,就可以使用此API—服務端和客戶端均可. 每個聲明兼容JSR 356的WebSocket協議,都必須實現這個API. 

          因此,開發人員可以自己編寫獨立于底層WebSocket實現的WebSocket應用。這是一個巨大的好處,因為它可以防止供應商鎖定,并允許更多的選擇、自由的庫、應用程序服務器。

          JSR 356是即將到來的java EE 7標準的一部分,因此,所有與Java EE 7兼容的應用服務器都有JSR 365標準WebSocket的實現.一旦建立,WebSocket客戶端和服務器節點已經是對稱的了。客戶端API與服務器端API的區別是很小的,JSR 356定義的Java client API只是Java EE7完整API的子集.

          客戶段-服務器端程序使用WebSockets,通常會包含一個服務器組件和多個客戶端組件, 如圖1所示:

          Figure 1

          圖1

          在這個例子中,server application 是通過Java編寫的,WebSocket 協議細節是由包含在Java EE 7容器中JSR 356 實現來處理的.

          JavaFX 客戶端可依賴任何與JSR 356兼容的客戶端實現來處理WebSocket協議問題. 

          其它客戶端(如,iOS 客戶端和HTML5客戶端)可使用其它 (非Java)與RFC6455兼容的實現來與server application通信.

          編程模型

          JSR 356定義的專家小組,希望支持Java EE開發人員常用的模式和技術。因此,JSR 356使用了注釋和注入。

          一般來說,支持兩種編程模型:

          • 注解驅動(annotation-driven). 通過使用注解POJOs, 開發者可與WebSocket生命周期事件交互.
          • 接口驅動(interface-driven). 開發者可實現Endpoint接口和與生命周期交互的方法.

          生命周期事件

          典型的WebSocket 交互生命周期如下:

          • 一端 (客戶端) 通過發送HTTP握手請求來初始化連接.
          • 其它端(服務端) 回復握手響應.
          • 建立連接.從現在開始,連接是完全對稱的.
          • 兩端都可發送和接收消息.
          • 其中一端關閉連接.

          大部分WebSocket生命周期事件都與Java方法對應,不管是 annotation-driven 還是interface-driven.

          Annotation-Driven 方式

          接受WebSocket請求的端點可以是以 @ServerEndpoint 注解的POJO. 

          此注解告知容器,此類應該被認為是WebSocket端點. 

          必須的value 元素指定了WebSocket端點的路徑.

          考慮下面的代碼片斷:

          @ServerEndpoint("/hello")  public class MyEndpoint { } 

          此代碼將會以相對路徑hello來發布一個端點.在后續方法調用中,此路徑可攜帶路徑參數,如: /hello/{userid}是一個有效路徑,在這里{userid} 的值,可在生命周期方法使用@PathParam 注解獲取.

          在GlassFish中,如果你的應用程序是用上下文mycontextroot 部署的,且在localhost的8080端口上監聽, WebSocket可通過使用ws://localhost:8080/mycontextroot/hello來訪問.

          初始化WebSocket連接的端點可以是以 @ClientEndpoint 注解的POJO.@ClientEndpoint 和 @ServerEndpoint的主要區別是ClientEndpoint 不接受路徑路值元素,因為它監聽進來的請求。

          @ClientEndpoint  public class MyClientEndpoint {} 

          Java中使用注解驅動POJO方式來初始化WebSocket連接,可通過如下代碼來完成:

          javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer();  container.conntectToServer(MyClientEndpoint.class, new URI("ws://localhost:8080/tictactoeserver/endpoint")); 

          此后,以 @ServerEndpoint 或@ClientEndpoint 注解的類都稱為注解端點.

          一旦建立了WebSocket連接 ,就會創建 Session,并且會調用注解端點中以@OnOpen注解的方法. 

          此方法包含了幾個參數:

          • javax.websocket.Session 參數, 代表創建的Session
          • EndpointConfig 實例包含了關于端點配置的信息
          • 0個或多個以 @PathParam注解的字符串參數,指的是端點路徑的path參數

          下面的方法實現了當打開WebSocket時,將會打印session的標識符:

          @OnOpen public void myOnOpen (Session session) {    System.out.println ("WebSocket opened: "+session.getId()); } 

          Session實例只要WebSocket未關閉就會一直有效Session類中包含了許多有意思的方法,以允許開發者獲取更多關于的信息

          同時,Session 也包含了應用程序特有的數據鉤子,即通過getUserProperties() 方法來返回 Map<String, Object>

          這允許開發者可以使用session-和需要在多個方法調用間共享的應用程序特定信息來填充Session實例.

          i當WebSocket端收到消息時,將會調用以@OnMessage注解的方法.以@OnMessage 注解的方法可包含下面的參數:

          • javax.websocket.Session 參數.
          • 0個或多個以 @PathParam注解的字符串參數,指的是端點路徑的path參數
          • 消息本身. 下面有可能消息類型描述.

          當其它端發送了文本消息時,下面的代碼片斷會打印消息內容:

          @OnMessage public void myOnMessage (String txt) {    System.out.println ("WebSocket received message: "+txt); }  

          如果以@OnMessage i注解的方法返回值不是void, WebSocket實現會將返回值發送給其它端點.下面的代碼片斷會將收到的文本消息以首字母大寫的形式發回給發送者:

          @OnMessage public String myOnMessage (String txt) {    return txt.toUpperCase(); }  

          另一種通過WebSocket連接來發送消息的代碼如下:

          RemoteEndpoint.Basic other = session.getBasicRemote(); other.sendText ("Hello, world"); 

          在這種方式中,我們從Session 對象開始,它可以從生命周期回調方法中獲取(例如,以 @OnOpen注解的方法).session實例上getBasicRemote() 方法返回的是WebSocket其它部分的代表RemoteEndpointRemoteEndpoint 實例可用于發送文本或其它類型的消息,后面有描述.

          當關閉WebSocket連接時,將會調用@OnClose 注解的方法。此方法接受下面的參數:

          • javax.websocket.Session 參數. 注意,一旦WebSocket真正關閉了,此參數就不能被使用了,這通常發生在@OnClose 注解方法返回之后.
          • javax.websocket.CloseReason 參數,用于描述關閉WebSocket的原因,如:正常關閉,協議錯誤,服務過載等等.
          • 0個或多個以 @PathParam注解的字符串參數,指的是端點路徑的path參數

          下面的代碼片段打印了WebSocket關閉的原因:

          @OnClose public void myOnClose (CloseReason reason) {    System.out.prinlnt ("Closing a WebSocket due to "+reason.getReasonPhrase()); } 

          完整情況下,這里還有一個生命周期注解:如果收到了錯誤,將會調用 @OnError 注解的方法。

          Interface-Driven 方式

          annotation-driven 方式允許我們注解一個Java類,以及使用生命周期注解來注解方法. 

          使用interface-driven方式,開發者可繼承javax.websocket.Endpoint 并覆蓋其中的onOpenonClose, 以及onError 方法:

          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) {...} } 

          為了攔截消息,需要在onOpen實現中注冊一個javax.websocket.MessageHandler:

          public void onOpen (Session session, EndpointConfig config) {    session.addMessageHandler (new MessageHandler() {...}); } 

          MessageHandler 接口有兩個子接口: MessageHandler.Partial和 MessageHandler.Whole

          MessageHandler.Partial 接口應該用于當開發者想要收到部分消息通知的時候,MessageHandler.Whole的實現應該用于整個消息到達通知

          下面的代碼片斷會監聽進來的文件消息,并將文本信息轉換為大小版本后發回給其它端點:

          public void onOpen (Session session, EndpointConfig config) {    final RemoteEndpoint.Basic remote = session.getBasicRemote();    session.addMessageHandler (new MessageHandler.Whole<String>() {       public void onMessage(String text) {                  try {                      remote.sendString(text.toUpperCase());                  } catch (IOException ioe) {                      // handle send failure here                  }              }     }); } 

          消息類型,編碼器,解碼器

          WebSocket的JavaAPI非常強大,因為它允許發送任或接收任何對象作為WebSocket消息.

          基本上,有三種不同類型的消息:

          • 基于文本的消息
          • 二進制消息
          • Pong 消息,它是WebSocket連接自身

          當使用interface-driven模式,每個session最多只能為這三個不同類型的消息注冊一個MessageHandler.

          當使用annotation-driven模式,針對不同類型的消息,只允許出現一個@onMessage 注解方法. 在注解方法中,消息內容中允許的參數依賴于消息類型。

          Javadoc for the @OnMessage annotation 明確指定了消息類型上允許出現的消息參數:

          • "如果方法用于處理文本消息: 

            • String 用于接收整個消息
            • Java 原型或等價的類用于接收整個消息并將其轉換為此類型
            • String 和 boolean 對用于部分接收消息
            • Reader 用于以阻塞流的方式接收整個消息
            • 端點的任何對象參數存在文本解碼器 (Decoder.Text 或 Decoder.TextStream).
          • 如果方法用于處理二進制消息: 

          • 如果方法是用于處理pong消息: 

          任何Java對象使用編碼器都可以編碼為基于文本或二進制的消息.這種基于文本或二進制的消息將轉輸到其它端點,在其它端點,它可以解碼成Java對象-或者被另外的WebSocket 包解釋. 

          通常情況下,XML或JSON用于來傳送WebSocket消息, 編碼/解碼然后會將Java對象編組成XML或JSON并在另一端解碼為Java對象.

          encoder是以javax.websocket.Encoder 接口的實現來定義,decoder是以javax.websocket.Decoder 接口的實現來定義的. 

          有時,端點實例必須知道encoders和decoders是什么.使用annotation-driven方式, 可向@ClientEndpoint 和 @ServerEndpoint l注解中的encode和decoder元素傳遞 encoders和decoders的列表。

          Listing 1 中的代碼展示了如何注冊一個 MessageEncoder 類(它定義了MyJavaObject實例到文本消息的轉換). MessageDecoder 是以相反的轉換來注冊的.

          @ServerEndpoint(value="/endpoint", encoders = MessageEncoder.class, decoders= MessageDecoder.class) public class MyEndpoint { ... }  class MessageEncoder implements Encoder.Text<MyJavaObject> {    @override    public String encode(MyJavaObject obj) throws EncodingException {       ...    } }  class MessageDecoder implements Decoder.Text<MyJavaObject> {    @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    } } 

          Listing 1

          Encoder 接口有多個子接口:

          • Encoder.Text 用于將Java對象轉成文本消息
          • Encoder.TextStream 用于將Java對象添加到字符流中
          • Encoder.Binary 用于將Java對象轉換成二進制消息
          • Encoder.BinaryStream 用于將Java對象添加到二進制流中

          類似地,Decoder 接口有四個子接口:

          • Decoder.Text 用于將文本消息轉換成Java對象
          • Decoder.TextStream 用于從字符流中讀取Java對象
          • Decoder.Binary 用于將二進制消息轉換成Java對象
          • Decoder.BinaryStream 用于從二進制流中讀取Java對象

          結論

          WebSocket Java API為Java開發者提供了標準API來集成IETF WebSocket標準.通過這樣做,Web 客戶端或本地客戶端可使用任何WebSocket實現來輕易地與Java后端通信。

          Java Api是高度可配置的,靈活的,它允許java開發者使用他們喜歡的模式。

          也可參考

          posted on 2016-07-24 01:35 胡小軍 閱讀(2774) 評論(0)  編輯  收藏 所屬分類: WebSocket

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 江山市| 南投县| 赤水市| 乌海市| 吉林市| 彝良县| 洛扎县| 肇州县| 晋宁县| 读书| 四会市| 鹤庆县| 新和县| 山丹县| 衡阳市| 施秉县| 正安县| 湛江市| 梓潼县| 临沧市| 南充市| 阳朔县| 邵阳市| 长丰县| 太湖县| 柯坪县| 梁平县| 茌平县| 顺平县| 特克斯县| 阿城市| 元谋县| 突泉县| 沐川县| 综艺| 凭祥市| 礼泉县| 开鲁县| 肇州县| 射洪县| 新源县|