一步步教你如何使用Java實(shí)現(xiàn)WebSocket
一、WebSocket簡(jiǎn)介
WebSocket協(xié)議通過在客戶端和服務(wù)端之間提供全雙工通信來進(jìn)行Web和服務(wù)器的交互功能。
在WebSocket應(yīng)用程序中,服務(wù)器發(fā)布WebSocket端點(diǎn),客戶端使用url連接到服務(wù)器。建立連接后,服務(wù)器和客戶端就可以互相發(fā)送消息。客戶端通常連接到一臺(tái)服務(wù)器,服務(wù)器接受多個(gè)客戶端的連接。
1.1 WebSocket協(xié)議
WebSocket協(xié)議有兩個(gè)部分:握手和傳輸。客戶端通過向服務(wù)端URL發(fā)送握手請(qǐng)求來建立連接。握手與現(xiàn)有的基于HTTP的基礎(chǔ)結(jié)構(gòu)相兼容。Web服務(wù)器將其解釋為升級(jí)版的HTTP連接請(qǐng)求。
一個(gè)客戶端建立連接的握手請(qǐng)求:
GET /path/to/websocket/endpoint HTTP/1.1 Host: localhost Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== Origin: http://localhost Sec-WebSocket-Version: 13
一個(gè)服務(wù)端響應(yīng):
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
從上面的請(qǐng)求和響應(yīng)中可以看出來,一個(gè)WebSocket連接的建立,需要客戶端和服務(wù)端維護(hù)一個(gè)Key來作為該連接的連接憑證。
客戶端向服務(wù)端發(fā)送WebSocketKey,服務(wù)器根據(jù)WebSocketKey生成WebSocketAccept返回給客戶端,客戶端對(duì)WebSocketKey的值再進(jìn)行相同的操作,如果與服務(wù)器返回的Accept的值相匹配,就表示握手成功。握手之后客戶端與服務(wù)端就互相發(fā)送消息。

1.2 WebSocket支持的消息類型
WebSocket支持文本消息(UTF-8編碼)和二進(jìn)制消息。WebSocket的控制消息由Close、Ping、Pong組成。ping和pong也有可能包含應(yīng)用程序信息。
webSocket端點(diǎn)具有如下的URI形式表示:
ws://host:port/path?query wss://host:port/path?query
- ws代表未加密的連接,默認(rèn)端口是80
- wss代表加密的連接,默認(rèn)端口是443
- path:表示服務(wù)器內(nèi)端點(diǎn)的位置
- query:路徑參數(shù)信息
二、創(chuàng)建一個(gè)WebSocket程序
創(chuàng)建和部署WebSocket端點(diǎn)的過程如下:
1、創(chuàng)建一個(gè)端點(diǎn)類
2、實(shí)現(xiàn)端點(diǎn)的生命周期方法
3、將業(yè)務(wù)邏輯添加到端點(diǎn)
4、將端點(diǎn)部署到Web應(yīng)用程序中
**注意:**與Servlet相反,WebSocket端點(diǎn)類會(huì)被實(shí)例化多次,容器針對(duì)與其部署的URI的每個(gè)連接都創(chuàng)建一個(gè)實(shí)例。每個(gè)實(shí)例都與一個(gè)連接相關(guān)聯(lián)。因?yàn)樵谌魏螘r(shí)間都只有一個(gè)線程執(zhí)行端點(diǎn)實(shí)例的代碼,所以有助于保持每個(gè)連接的用戶狀態(tài)簡(jiǎn)化開發(fā)。
2.1 生命周期方法
EndPoint類中定義了三個(gè)生命周期方法: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());
}
}注意:一個(gè)服務(wù)類最多可以有三個(gè)OnMessage注解,每個(gè)消息類型分別使用一種方法:Text、Binary、Pong
2.3 維護(hù)客戶狀態(tài)
有時(shí)在程序中我們可能需要在連接中維護(hù)一些用戶參數(shù),WebSocket也提供了這樣的服務(wù)
Session.getUserProperties獲取用戶參數(shù)信息
如果要存儲(chǔ)所有連接的客戶端共有的信息,可以使用靜態(tài)變量,但是需要用戶保證對(duì)數(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的方式解決。
同時(shí)由于WebSocket的@Message注解只能有一個(gè)用來傳輸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、在端點(diǎn)類注解中添加該Encoder
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }
)
public class EncEndpoint { ... }3、這時(shí)候就可以發(fā)送MessageA和MessageB兩種類型的Text數(shù)據(jù)
MessageA msgA = new MessageA(...); MessageB msgB = new MessageB(...); session.getBasicRemote.sendObject(msgA); session.getBasicRemote.sendObject(msgB);
注意:webSocket會(huì)自動(dòng)尋找使用哪種編碼器,所以發(fā)送數(shù)據(jù)統(tǒng)一使用sendObject即可
Decoder
實(shí)現(xiàn)Decoder以將WebSocket消息轉(zhuǎn)換為Java對(duì)象
Decoder.Text 用于文本消息
Decoder.Binary 用于二進(jìn)制消息
使用方法
與Encoder類似
注意:與Encoder不同,Decoder最多可以指定一個(gè)Binary和一個(gè)Text類型的Decoder,如果有兩種以上的Java類型作為文本消息進(jìn)行發(fā)送和接收需要進(jìn)行定義處理。可以使多個(gè)消息繼承一個(gè)公共的消息父類
1、編寫Decoder類,對(duì)收到消息類型的不同進(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、在端點(diǎn)類中添加decoders={MessageDecoder.class}
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },
decoders = { MessageTextDecoder.class }
)
public class EncDecEndpoint { ... }3、在@OnMessage方法中使用
@OnMessage
public 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...
}
}總結(jié)
到此這篇關(guān)于如何使用Java實(shí)現(xiàn)WebSocket的文章就介紹到這了,更多相關(guān)Java實(shí)現(xiàn)WebSocket內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java中使用WebSocket的幾種方式
- Java實(shí)現(xiàn)使用Websocket發(fā)送消息詳細(xì)代碼舉例
- 如何在Java中使用WebSocket協(xié)議
- springboot整合websocket后啟動(dòng)報(bào)錯(cuò)(javax.websocket.server.ServerContainer not available)
- Java WebSocket客戶端接收大量數(shù)據(jù)的三種方案
- 使用Java WebSocket獲取客戶端IP地址的示例代碼
- Java中實(shí)現(xiàn)WebSocket方法詳解
- 教你如何使用Java實(shí)現(xiàn)WebSocket
- java后端+前端使用WebSocket實(shí)現(xiàn)消息推送的詳細(xì)流程
- JAVA 日常開發(fā)中Websocket示例詳解
相關(guān)文章
Javaweb動(dòng)態(tài)開發(fā)最重要的Servlet詳解
動(dòng)態(tài)web的核心是Servlet,由tomcat解析并執(zhí)行,本質(zhì)是Java中的一個(gè)類(面向?qū)ο螅┻@個(gè)類的功能十分強(qiáng)大幾乎可以完成全部功能,在Java規(guī)范中只有Servlet實(shí)現(xiàn)類實(shí)例化的對(duì)象才能被瀏覽器訪問,所以掌握Servlet具有重要意義2022-08-08
Spring組件初始化擴(kuò)展點(diǎn)BeanPostProcessor的作用詳解
本文通過實(shí)戰(zhàn)案例和常見應(yīng)用場(chǎng)景詳細(xì)介紹了BeanPostProcessor的使用,并強(qiáng)調(diào)了其在Spring擴(kuò)展中的重要性,感興趣的朋友一起看看吧2025-03-03
java -jar example.jar 產(chǎn)生的日志輸出到指定文件的方法
這篇文章給大家介紹java -jar example.jar 產(chǎn)生的日志輸出到指定文件的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,感興趣的朋友一起看看吧2025-08-08
Java分布式事務(wù)實(shí)現(xiàn)原理與解決方案詳解
分布式事務(wù)是分布式系統(tǒng)中的核心挑戰(zhàn)之一,它確保跨多個(gè)獨(dú)立服務(wù)或數(shù)據(jù)源的操作能夠保持原子性、一致性、隔離性和持久性(ACID),本文給大家介紹Java生態(tài)中分布式事務(wù)的實(shí)現(xiàn)原理、主流解決方案及其適用場(chǎng)景,感興趣的朋友一起看看吧2025-09-09
springboot與vue詳解實(shí)現(xiàn)短信發(fā)送流程
隨著人工智能的不斷發(fā)展,機(jī)器學(xué)習(xí)這門技術(shù)也越來越重要,很多人都開啟了學(xué)習(xí)機(jī)器學(xué)習(xí),本文就介紹了機(jī)器學(xué)習(xí)的基礎(chǔ)內(nèi)容2022-06-06
一個(gè)Servlet是如何處理多個(gè)請(qǐng)求的?
以前我一直以為一個(gè)Servlet只能處理一個(gè)請(qǐng)求,后來發(fā)現(xiàn)是自己太菜了,可以借助攜帶一個(gè)參數(shù)來完成多個(gè)請(qǐng)求的處理,根據(jù)參數(shù)的不同,在核心的service方法中調(diào)用不同的業(yè)務(wù)方法,來實(shí)現(xiàn)處理多個(gè)servlet請(qǐng)求的目的,廢話不多說,直接上代碼,需要的朋友可以參考下2021-06-06
SpringBoot和Vue.js實(shí)現(xiàn)的前后端分離的用戶權(quán)限管理系統(tǒng)
本文主要介紹了SpringBoot和Vue.js實(shí)現(xiàn)的前后端分離的用戶權(quán)限管理系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Java中@Accessors使用的實(shí)現(xiàn)示例
本文主要介紹了Java中@Accessors使用的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-11-11
Java結(jié)合Kotlin實(shí)現(xiàn)寶寶年齡計(jì)算
這篇文章主要為大家介紹了Java結(jié)合Kotlin實(shí)現(xiàn)寶寶年齡計(jì)算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

