教你怎么使用Java實現(xiàn)WebSocket
WebSocket協(xié)議通過在客戶端和服務(wù)端之間提供全雙工通信來進(jìn)行Web和服務(wù)器的交互功能。
在WebSocket應(yīng)用程序中,服務(wù)器發(fā)布WebSocket端點,客戶端使用url連接到服務(wù)器。建立連接后,服務(wù)器和客戶端就可以互相發(fā)送消息。客戶端通常連接到一臺服務(wù)器,服務(wù)器接受多個客戶端的連接。
1.1 WebSocket協(xié)議WebSocket協(xié)議有兩個部分:握手和傳輸。客戶端通過向服務(wù)端URL發(fā)送握手請求來建立連接。握手與現(xiàn)有的基于HTTP的基礎(chǔ)結(jié)構(gòu)相兼容。Web服務(wù)器將其解釋為升級版的HTTP連接請求。一個客戶端建立連接的握手請求:
GET /path/to/websocket/endpoint HTTP/1.1Host: localhostUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==Origin: http://localhostSec-WebSocket-Version: 13
一個服務(wù)端響應(yīng):
HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
從上面的請求和響應(yīng)中可以看出來,一個WebSocket連接的建立,需要客戶端和服務(wù)端維護(hù)一個Key來作為該連接的連接憑證。客戶端向服務(wù)端發(fā)送WebSocketKey,服務(wù)器根據(jù)WebSocketKey生成WebSocketAccept返回給客戶端,客戶端對WebSocketKey的值再進(jìn)行相同的操作,如果與服務(wù)器返回的Accept的值相匹配,就表示握手成功。握手之后客戶端與服務(wù)端就互相發(fā)送消息。
WebSocket支持文本消息(UTF-8編碼)和二進(jìn)制消息。WebSocket的控制消息由Close、Ping、Pong組成。ping和pong也有可能包含應(yīng)用程序信息。webSocket端點具有如下的URI形式表示:
ws://host:port/path?querywss://host:port/path?query
ws代表未加密的連接,默認(rèn)端口是80wss代表加密的連接,默認(rèn)端口是443path:表示服務(wù)器內(nèi)端點的位置query:路徑參數(shù)信息
二、創(chuàng)建一個WebSocket程序創(chuàng)建和部署WebSocket端點的過程如下:1、創(chuàng)建一個端點類2、實現(xiàn)端點的生命周期方法3、將業(yè)務(wù)邏輯添加到端點4、將端點部署到Web應(yīng)用程序中
注意:與Servlet相反,WebSocket端點類會被實例化多次,容器針對與其部署的URI的每個連接都創(chuàng)建一個實例。每個實例都與一個連接相關(guān)聯(lián)。因為在任何時間都只有一個線程執(zhí)行端點實例的代碼,所以有助于保持每個連接的用戶狀態(tài)簡化開發(fā)。
2.1 生命周期方法EndPoint類中定義了三個生命周期方法:onOpen、onClose、onError使用注解方式創(chuàng)建服務(wù)類
@ServerEndpoint('/echo')public class EchoEndpoint { @OnMessage public void onMessage(Session session, String msg) { try { session.getBasicRemote().sendText(msg); } catch (IOException e) { ... } }}2.2 業(yè)務(wù)邏輯方法
@ServerEndpoint('/receive')public class ReceiveEndpoint { @OnMessage public void textMessage(Session session, String msg) { System.out.println('Text message: ' + msg); } @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()); }}
注意:一個服務(wù)類最多可以有三個OnMessage注解,每個消息類型分別使用一種方法:Text、Binary、Pong
2.3 維護(hù)客戶狀態(tài)有時在程序中我們可能需要在連接中維護(hù)一些用戶參數(shù),WebSocket也提供了這樣的服務(wù)Session.getUserProperties獲取用戶參數(shù)信息如果要存儲所有連接的客戶端共有的信息,可以使用靜態(tài)變量,但是需要用戶保證對數(shù)據(jù)的線程安全訪問。
@ServerEndpoint('/delayedecho')public class DelayedEchoEndpoint { @OnOpen public void open(Session session) { session.getUserProperties().put('previousMsg', ' '); } @OnMessage public void message(Session session, String msg) { String prev = (String) session.getUserProperties() .get('previousMsg'); session.getUserProperties().put('previousMsg', msg); try { session.getBasicRemote().sendText(prev); } catch (IOException e) { ... } }}2.4 數(shù)據(jù)格式的編碼與解碼
由于客戶端和服務(wù)端交互可能涉及數(shù)據(jù)格式的轉(zhuǎn)換,所以提供了Decoder和Encoder的方式解決。同時由于WebSocket的@Message注解只能有一個用來傳輸Text信息或Binary信息,所以要進(jìn)行最常用的Json->entity轉(zhuǎn)換解析就需要該方法Encoder
Encoder.Text 用于文本消息Encoder.Binary 用于二進(jìn)制消息
使用方法:
1、創(chuàng)建編解碼類
public class MessageATextEncoder implements Encoder.Text<MessageA> { @Override public void init(EndpointConfig ec) { } @Override public void destroy() { } @Override public String encode(MessageA msgA) throws EncodeException { // Access msgA’s properties and convert to JSON text... return msgAJsonString; }}
2、在端點類注解中添加該Encoder
@ServerEndpoint( value = '/myendpoint', encoders = { MessageATextEncoder.class, MessageBTextEncoder.class })public class EncEndpoint { ... }
3、這時候就可以發(fā)送MessageA和MessageB兩種類型的Text數(shù)據(jù)
MessageA msgA = new MessageA(...);MessageB msgB = new MessageB(...);session.getBasicRemote.sendObject(msgA);session.getBasicRemote.sendObject(msgB);
注意:webSocket會自動尋找使用哪種編碼器,所以發(fā)送數(shù)據(jù)統(tǒng)一使用sendObject即可
Decoder
實現(xiàn)Decoder以將WebSocket消息轉(zhuǎn)換為Java對象
Decoder.Text 用于文本消息 Decoder.Binary 用于二進(jìn)制消息使用方法
與Encoder類似
注意:與Encoder不同,Decoder最多可以指定一個Binary和一個Text類型的Decoder,如果有兩種以上的Java類型作為文本消息進(jìn)行發(fā)送和接收需要進(jìn)行定義處理。可以使多個消息繼承一個公共的消息父類
1、編寫Decoder類,對收到消息類型的不同進(jìn)行不同的解碼
public class MessageTextDecoder implements Decoder.Text<Message> { @Override public void init(EndpointConfig ec) { } @Override public void destroy() { } @Override public Message decode(String string) throws DecodeException { // Read message... if ( /* message is an A message */ ) return new MessageA(...); else if ( /* message is a B message */ ) return new MessageB(...); } @Override public boolean willDecode(String string) { // Determine if the message can be converted into either a // MessageA object or a MessageB object... return canDecode; }}
2、在端點類中添加decoders={MessageDecoder.class}
@ServerEndpoint( value = '/myendpoint', encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }, decoders = { MessageTextDecoder.class })public class EncDecEndpoint { ... }
3、在@OnMessage方法中使用
@OnMessagepublic void message(Session session, Message msg) { if (msg instanceof MessageA) { // We received a MessageA object... } else if (msg instanceof MessageB) { // We received a MessageB object... }}
到此這篇關(guān)于教你怎么使用Java WebSocket的文章就介紹到這了,更多相關(guān)Java WebSocket詳解內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. IntelliJ IDEA刪除類的方法步驟2. JSP中Servlet的Request與Response的用法與區(qū)別3. Struts2獲取參數(shù)的三種方法總結(jié)4. vue使用moment如何將時間戳轉(zhuǎn)為標(biāo)準(zhǔn)日期時間格式5. Android 實現(xiàn)徹底退出自己APP 并殺掉所有相關(guān)的進(jìn)程6. IntelliJ IDEA導(dǎo)入jar包的方法7. js select支持手動輸入功能實現(xiàn)代碼8. vue cli4下環(huán)境變量和模式示例詳解9. Django視圖類型總結(jié)10. Xml簡介_動力節(jié)點Java學(xué)院整理
