<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    常用鏈接

    留言簿

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

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

    學(xué)習(xí)如何在你的應(yīng)用程序中集成WebSockets.

    Published April 2013

    對(duì)于許多基于客戶端-服務(wù)器程序來(lái)說(shuō),老的HTTP 請(qǐng)求-響應(yīng)模型已經(jīng)有它的局限性. 信息必須通過(guò)多次請(qǐng)求才能將其從服務(wù)端傳送到客戶端.

    過(guò)去許多的黑客使用某些技術(shù)來(lái)繞過(guò)這個(gè)問(wèn)題,例如:長(zhǎng)輪詢(long polling)、基于 HTTP 長(zhǎng)連接的服務(wù)器推技術(shù)(Comet)

    然而,基于標(biāo)準(zhǔn)的、雙向的、客戶端和服務(wù)器之間全雙工的信道需求再不斷增加。

    在2011年, IETF發(fā)布了標(biāo)準(zhǔn)WebSocket協(xié)議-RFC 6455. 從那時(shí)起,大多數(shù)Web瀏覽器都實(shí)現(xiàn)了支持WebSocket協(xié)議的客戶端APIs.同時(shí),許多Java 包也開(kāi)始實(shí)現(xiàn)了WebSocket協(xié)議.

    WebSocket協(xié)議利用HTTP升級(jí)技術(shù)來(lái)將HTTP連接升級(jí)到WebSocket. 一旦升級(jí)后,連接就有了在兩個(gè)方向上相互獨(dú)立(全雙式)發(fā)送消息(數(shù)據(jù)楨)的能力. 

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

    額外的headers常常會(huì)使開(kāi)銷大于有效負(fù)載(payload)。

    JSR 356

    JSR 356, WebSocket的Java API, 明確規(guī)定了API,當(dāng)Java開(kāi)發(fā)者需要在應(yīng)用程序中集成WebSocket時(shí),就可以使用此API—服務(wù)端和客戶端均可. 每個(gè)聲明兼容JSR 356的WebSocket協(xié)議,都必須實(shí)現(xiàn)這個(gè)API. 

    因此,開(kāi)發(fā)人員可以自己編寫(xiě)?yīng)毩⒂诘讓覹ebSocket實(shí)現(xiàn)的WebSocket應(yīng)用。這是一個(gè)巨大的好處,因?yàn)樗梢苑乐构?yīng)商鎖定,并允許更多的選擇、自由的庫(kù)、應(yīng)用程序服務(wù)器。

    JSR 356是即將到來(lái)的java EE 7標(biāo)準(zhǔn)的一部分,因此,所有與Java EE 7兼容的應(yīng)用服務(wù)器都有JSR 365標(biāo)準(zhǔn)WebSocket的實(shí)現(xiàn).一旦建立,WebSocket客戶端和服務(wù)器節(jié)點(diǎn)已經(jīng)是對(duì)稱的了。客戶端API與服務(wù)器端API的區(qū)別是很小的,JSR 356定義的Java client API只是Java EE7完整API的子集.

    客戶段-服務(wù)器端程序使用WebSockets,通常會(huì)包含一個(gè)服務(wù)器組件和多個(gè)客戶端組件, 如圖1所示:

    Figure 1

    圖1

    在這個(gè)例子中,server application 是通過(guò)Java編寫(xiě)的,WebSocket 協(xié)議細(xì)節(jié)是由包含在Java EE 7容器中JSR 356 實(shí)現(xiàn)來(lái)處理的.

    JavaFX 客戶端可依賴任何與JSR 356兼容的客戶端實(shí)現(xiàn)來(lái)處理WebSocket協(xié)議問(wèn)題. 

    其它客戶端(如,iOS 客戶端和HTML5客戶端)可使用其它 (非Java)與RFC6455兼容的實(shí)現(xiàn)來(lái)與server application通信.

    編程模型

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

    一般來(lái)說(shuō),支持兩種編程模型:

    • 注解驅(qū)動(dòng)(annotation-driven). 通過(guò)使用注解POJOs, 開(kāi)發(fā)者可與WebSocket生命周期事件交互.
    • 接口驅(qū)動(dòng)(interface-driven). 開(kāi)發(fā)者可實(shí)現(xiàn)Endpoint接口和與生命周期交互的方法.

    生命周期事件

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

    • 一端 (客戶端) 通過(guò)發(fā)送HTTP握手請(qǐng)求來(lái)初始化連接.
    • 其它端(服務(wù)端) 回復(fù)握手響應(yīng).
    • 建立連接.從現(xiàn)在開(kāi)始,連接是完全對(duì)稱的.
    • 兩端都可發(fā)送和接收消息.
    • 其中一端關(guān)閉連接.

    大部分WebSocket生命周期事件都與Java方法對(duì)應(yīng),不管是 annotation-driven 還是interface-driven.

    Annotation-Driven 方式

    接受WebSocket請(qǐng)求的端點(diǎn)可以是以 @ServerEndpoint 注解的POJO. 

    此注解告知容器,此類應(yīng)該被認(rèn)為是WebSocket端點(diǎn). 

    必須的value 元素指定了WebSocket端點(diǎn)的路徑.

    考慮下面的代碼片斷:

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

    此代碼將會(huì)以相對(duì)路徑hello來(lái)發(fā)布一個(gè)端點(diǎn).在后續(xù)方法調(diào)用中,此路徑可攜帶路徑參數(shù),如: /hello/{userid}是一個(gè)有效路徑,在這里{userid} 的值,可在生命周期方法使用@PathParam 注解獲取.

    在GlassFish中,如果你的應(yīng)用程序是用上下文mycontextroot 部署的,且在localhost的8080端口上監(jiān)聽(tīng), WebSocket可通過(guò)使用ws://localhost:8080/mycontextroot/hello來(lái)訪問(wèn).

    初始化WebSocket連接的端點(diǎn)可以是以 @ClientEndpoint 注解的POJO.@ClientEndpoint 和 @ServerEndpoint的主要區(qū)別是ClientEndpoint 不接受路徑路值元素,因?yàn)樗O(jiān)聽(tīng)進(jìn)來(lái)的請(qǐng)求。

    @ClientEndpoint  public class MyClientEndpoint {} 

    Java中使用注解驅(qū)動(dòng)POJO方式來(lái)初始化WebSocket連接,可通過(guò)如下代碼來(lái)完成:

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

    此后,以 @ServerEndpoint 或@ClientEndpoint 注解的類都稱為注解端點(diǎn).

    一旦建立了WebSocket連接 ,就會(huì)創(chuàng)建 Session,并且會(huì)調(diào)用注解端點(diǎn)中以@OnOpen注解的方法. 

    此方法包含了幾個(gè)參數(shù):

    • javax.websocket.Session 參數(shù), 代表創(chuàng)建的Session
    • EndpointConfig 實(shí)例包含了關(guān)于端點(diǎn)配置的信息
    • 0個(gè)或多個(gè)以 @PathParam注解的字符串參數(shù),指的是端點(diǎn)路徑的path參數(shù)

    下面的方法實(shí)現(xiàn)了當(dāng)打開(kāi)WebSocket時(shí),將會(huì)打印session的標(biāo)識(shí)符:

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

    Session實(shí)例只要WebSocket未關(guān)閉就會(huì)一直有效Session類中包含了許多有意思的方法,以允許開(kāi)發(fā)者獲取更多關(guān)于的信息

    同時(shí),Session 也包含了應(yīng)用程序特有的數(shù)據(jù)鉤子,即通過(guò)getUserProperties() 方法來(lái)返回 Map<String, Object>

    這允許開(kāi)發(fā)者可以使用session-和需要在多個(gè)方法調(diào)用間共享的應(yīng)用程序特定信息來(lái)填充Session實(shí)例.

    i當(dāng)WebSocket端收到消息時(shí),將會(huì)調(diào)用以@OnMessage注解的方法.以@OnMessage 注解的方法可包含下面的參數(shù):

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

    當(dāng)其它端發(fā)送了文本消息時(shí),下面的代碼片斷會(huì)打印消息內(nèi)容:

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

    如果以@OnMessage i注解的方法返回值不是void, WebSocket實(shí)現(xiàn)會(huì)將返回值發(fā)送給其它端點(diǎn).下面的代碼片斷會(huì)將收到的文本消息以首字母大寫(xiě)的形式發(fā)回給發(fā)送者:

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

    另一種通過(guò)WebSocket連接來(lái)發(fā)送消息的代碼如下:

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

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

    當(dāng)關(guān)閉WebSocket連接時(shí),將會(huì)調(diào)用@OnClose 注解的方法。此方法接受下面的參數(shù):

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

    下面的代碼片段打印了WebSocket關(guān)閉的原因:

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

    完整情況下,這里還有一個(gè)生命周期注解:如果收到了錯(cuò)誤,將會(huì)調(diào)用 @OnError 注解的方法。

    Interface-Driven 方式

    annotation-driven 方式允許我們注解一個(gè)Java類,以及使用生命周期注解來(lái)注解方法. 

    使用interface-driven方式,開(kāi)發(fā)者可繼承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實(shí)現(xiàn)中注冊(cè)一個(gè)javax.websocket.MessageHandler:

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

    MessageHandler 接口有兩個(gè)子接口: MessageHandler.Partial和 MessageHandler.Whole

    MessageHandler.Partial 接口應(yīng)該用于當(dāng)開(kāi)發(fā)者想要收到部分消息通知的時(shí)候,MessageHandler.Whole的實(shí)現(xiàn)應(yīng)該用于整個(gè)消息到達(dá)通知

    下面的代碼片斷會(huì)監(jiān)聽(tīng)進(jìn)來(lái)的文件消息,并將文本信息轉(zhuǎn)換為大小版本后發(fā)回給其它端點(diǎn):

    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非常強(qiáng)大,因?yàn)樗试S發(fā)送任或接收任何對(duì)象作為WebSocket消息.

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

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

    當(dāng)使用interface-driven模式,每個(gè)session最多只能為這三個(gè)不同類型的消息注冊(cè)一個(gè)MessageHandler.

    當(dāng)使用annotation-driven模式,針對(duì)不同類型的消息,只允許出現(xiàn)一個(gè)@onMessage 注解方法. 在注解方法中,消息內(nèi)容中允許的參數(shù)依賴于消息類型。

    Javadoc for the @OnMessage annotation 明確指定了消息類型上允許出現(xiàn)的消息參數(shù):

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

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

      • byte[] 或 ByteBuffer 用于接收整個(gè)消息
      • byte[] 和 boolean 對(duì), 或者 ByteBuffer 和boolean對(duì)用于部分接收消息
      • InputStream 用于按阻塞流的方式接收整個(gè)消息
      • 端點(diǎn)的任何對(duì)象參數(shù)存在二進(jìn)制解碼器(Decoder.Binary or Decoder.BinaryStream).
    • 如果方法是用于處理pong消息: 

    任何Java對(duì)象使用編碼器都可以編碼為基于文本或二進(jìn)制的消息.這種基于文本或二進(jìn)制的消息將轉(zhuǎn)輸?shù)狡渌它c(diǎn),在其它端點(diǎn),它可以解碼成Java對(duì)象-或者被另外的WebSocket 包解釋. 

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

    encoder是以javax.websocket.Encoder 接口的實(shí)現(xiàn)來(lái)定義,decoder是以javax.websocket.Decoder 接口的實(shí)現(xiàn)來(lái)定義的. 

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

    Listing 1 中的代碼展示了如何注冊(cè)一個(gè) MessageEncoder 類(它定義了MyJavaObject實(shí)例到文本消息的轉(zhuǎn)換). MessageDecoder 是以相反的轉(zhuǎn)換來(lái)注冊(cè)的.

    @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 接口有多個(gè)子接口:

    • Encoder.Text 用于將Java對(duì)象轉(zhuǎn)成文本消息
    • Encoder.TextStream 用于將Java對(duì)象添加到字符流中
    • Encoder.Binary 用于將Java對(duì)象轉(zhuǎn)換成二進(jìn)制消息
    • Encoder.BinaryStream 用于將Java對(duì)象添加到二進(jìn)制流中

    類似地,Decoder 接口有四個(gè)子接口:

    • Decoder.Text 用于將文本消息轉(zhuǎn)換成Java對(duì)象
    • Decoder.TextStream 用于從字符流中讀取Java對(duì)象
    • Decoder.Binary 用于將二進(jìn)制消息轉(zhuǎn)換成Java對(duì)象
    • Decoder.BinaryStream 用于從二進(jìn)制流中讀取Java對(duì)象

    結(jié)論

    WebSocket Java API為Java開(kāi)發(fā)者提供了標(biāo)準(zhǔn)API來(lái)集成IETF WebSocket標(biāo)準(zhǔn).通過(guò)這樣做,Web 客戶端或本地客戶端可使用任何WebSocket實(shí)現(xiàn)來(lái)輕易地與Java后端通信。

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

    也可參考

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

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲男人的天堂在线va拉文| 国国内清清草原免费视频99| 好爽好紧好大的免费视频国产| 亚洲一区二区三区在线| 日韩人妻无码精品久久免费一| 亚洲av无码专区国产乱码在线观看| 一级成人a免费视频| 国产亚洲精品国看不卡| 两个人的视频www免费| 亚洲va久久久噜噜噜久久天堂| 永久免费av无码入口国语片| 亚洲国产一区在线| 91福利免费视频| 亚洲久悠悠色悠在线播放| 永久免费视频v片www| 色视频在线观看免费| 亚洲色成人中文字幕网站| 久久香蕉国产线看免费| 亚洲成人福利在线观看| 国产一精品一AV一免费孕妇| 亚洲av无码成人精品国产| 亚洲国产一区视频| 在线看片免费人成视频播| 亚洲中文无码线在线观看| 波多野结衣久久高清免费 | 亚洲色偷偷综合亚洲av78| 国产zzjjzzjj视频全免费| 巨胸喷奶水www永久免费| 蜜芽亚洲av无码精品色午夜| 免费看黄视频网站| 国产成人精品亚洲| 亚洲AV无码久久精品狠狠爱浪潮| 一级毛片在线观看免费| 亚洲欧美精品午睡沙发| 亚洲午夜精品久久久久久浪潮 | 亚洲Av永久无码精品黑人 | 91免费精品国自产拍在线不卡| 亚洲av无码兔费综合| 久久亚洲AV午夜福利精品一区| 四虎永久在线精品免费网址| 中国黄色免费网站|