一、WebSocket
1.1、WebSocket概念
WebSocket是一種協議,用于在Web應用程序和服務器之間建立實時、雙向的通信連接。它通過一個單一的TCP連接提供了持久化連接,這使得Web應用程序可以更加實時地傳遞數據。WebSocket協議最初由W3C開發,并于2011年成為標準。
1.2、WebSocket協議
WebSocket 協議是一種基于TCP的協議,用于在客戶端和服務器之間建立持久連接,并且可以在這個連接上實時地交換數據。WebSocket協議有自己的握手協議,用于建立連接,也有自己的數據傳輸格式。
當客戶端發送一個 WebSocket 請求時,服務器將發送一個協議響應以確認請求。在握手期間,客戶端和服務器將協商使用的協議版本、支持的子協議、支持的擴展選項等。一旦握手完成,連接將保持打開狀態,客戶端和服務器就可以在連接上實時地傳遞數據。
WebSocket 協議使用的是雙向數據傳輸,即客戶端和服務器都可以在任意時間向對方發送數據,而不需要等待對方的請求。它支持二進制數據和文本數據,可以自由地在它們之間進行轉換。
總之,WebSocket協議是一種可靠的、高效的、雙向的、持久的通信協議,它適用于需要實時通信的Web應用程序,如在線游戲、實時聊天等
1.3、WebSocket原理
WebSocket 生命周期描述了 WebSocket 連接從創建到關閉的過程。一個 WebSocket 連接包含以下四個主要階段:
- 連接建立階段(Connection Establishment):在這個階段,客戶端和服務器之間的 WebSocket 連接被建立。客戶端發送一個 WebSocket 握手請求,服務器響應一個握手響應,然后連接就被建立了。
- 連接開放階段(Connection Open):在這個階段,WebSocket 連接已經建立并開放,客戶端和服務器可以在連接上互相發送數據。
- 連接關閉階段(Connection Closing):在這個階段,一個 WebSocket 連接即將被關閉。它可以被客戶端或服務器發起,通過發送一個關閉幀來關閉連接。
- 連接關閉完成階段(Connection Closed):在這個階段,WebSocket 連接已經完全關閉。客戶端和服務器之間的任何交互都將無效
客戶端依靠發起HTTP握手,告訴服務端進行WebSocket協議通訊,并告知WebSocket協議版本。服務端確認協議版本,升級為WebSocket協議。之后如果有數據需要推送,會主動推送給客戶端
請求頭Request Headers
GET /test HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: sehfiowqweuq1psd==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp
Origin: http://hello.com
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Version: 13
首先客戶端(如瀏覽器)發出帶有特殊消息頭(Upgrade、Connection)的請求到服務器,服務器判斷是否支持升級,支持則返回響應狀態碼101,表示協議升級成功,對于WebSocket就是握手成功。其中關鍵的字段就是Upgrade,Connection,告訴 Apache 、 Nginx 等服務器:注意啦,發起的是Websocket協議,不再 使用原先的HTTP。其中,Sec-WebSocket-Key當成是請求id就好了。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HaA6EjhHRejpHyuO0yBnY4J4n3A=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
Sec-WebSocket-Protocol: v12.stomp
Sec-WebSocket-Accept的字段值是由握手請求中的Sec-WebSocket-Key的字段值生成的。成功握手確立WebSocket連接之后,通信時不再使用HTTP的數據幀,而采用WebSocket獨立的數據幀
1.4、 應用場景
實時聊天:聊天應用需要實時的消息傳遞,WebSocket 提供了高效的解決方案。
在線游戲:游戲中的實時交互(例如玩家動作和狀態更新)可以通過 WebSocket 高效地處理。
股票市場:股票和金融市場應用需要實時更新數據,WebSocket 能夠提供實時行情和交易信息。
實時通知:例如社交網絡應用中的即時通知和更新。
協作應用:如實時文檔編輯和在線協作工具,可以使用 WebSocket 實現多用戶之間的同步更新。
WebSocket 協議在許多現代網絡應用中扮演了重要角色,特別是在需要高頻率數據交換和低延遲響應的場景中
1.5、WebSocket優劣勢
WebSocket優勢:
- 實時性:由于WebSocket的持久化連接,它可以實現實時的數據傳輸,避免了Web應用程序需要不斷地發送請求以獲取最新數據的情況。
- 雙向通信:WebSocket協議支持雙向通信,這意味著服務器可以主動向客戶端發送數據,而不需要客戶端發送請求。
- 減少網絡負載:由于WebSocket的持久化連接,它可以減少HTTP請求的數量,從而減少了網絡負載。
WebSocket劣勢:
- 需要瀏覽器和服務器都支持:WebSocket是一種相對新的技術,需要瀏覽器和服務器都支持。一些舊的瀏覽器和服務器可能不支持WebSocket。
- 需要額外的開銷:WebSocket需要在服務器上維護長時間的連接,這需要額外的開銷,包括內存和CPU。
- 安全問題:由于WebSocket允許服務器主動向客戶端發送數據,可能會存在安全問題。服務器必須保證只向合法的客戶端發送數據
1.6、http和websocket區別
(1)WebSocket是雙向通信協議,模擬Socket協議,可以雙向發送或接受信息;HTTP是單向的
(2)WebSocket是需要瀏覽器和服務器握手進行建立連接的;而HTTP是瀏覽器發起向服務器的連接,服務器預先并不知道這個連接
二、Java中使用WebSocket
2.1、Java WebSocket API編寫WebSocket
2.1.1、WebSocket服務器的搭建方法
在Java中搭建WebSocket服務器的方法如下:
1、首先,需要引入Java WebSocket API庫。可以從Maven倉庫下載Java WebSocket API依賴,或者直接將相關jar文件添加到項目中。
2、創建一個WebSocket服務器類,實現javax.websocket.Endpoint接口。這個類將作為WebSocket服務器的入口點,并定義了處理WebSocket連接和消息的方法。
3、在服務器類中,重寫以下方法:
@OnOpen:注解用于標記一個方法,當WebSocket與客戶端成功建立連接時,這個方法將被調用。
@OnMessage:注解標記的方法會在收到客戶端發送的消息時被調用。在示例中,收到消息后會打印消息內容,并將響應消息發送回客戶端。
@OnClose:注解標記的方法會在WebSocket關閉時被調用。
@OnError:注解標記的方法會在發生錯誤時被調用。
在類上使用@ServerEndpoint注解,將這個類聲明為一個WebSocket端點。/websocket是WebSocket的URI,客戶端可以通過這個URI來連接到這個WebSocket端點。
4、使用javax.websocket.server.ServerEndpoint注解標記服務器類,指定WebSocket的URL路徑。
5、創建一個WebSocket服務器容器類,用來啟動和管理WebSocket服務器。可以使用javax.websocket.server.ServerEndpointConfig的Builder類創建一個服務器配置對象,將服務器類和URL路徑關聯起來,并配置其他相關信息。
6、使用WebSocket服務器容器類的create方法創建一個WebSocket服務器實例。
7、啟動WebSocket服務器,可以使用Server.start()方法
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;@ServerEndpoint("/websocket")
public class WebSocketServer {@OnOpenpublic void onOpen(Session session) {// 在連接打開時執行的操作}@OnClosepublic void onClose(Session session) {// 在連接關閉時執行的操作}@OnMessagepublic void onMessage(String message, Session session) {// 處理收到的消息}
}
編寫配置類
// 需要注入Bean的話必須聲明為配置類
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}
}
?調用:
import javax.websocket.server.ServerEndpointConfig;public class WebSocketServerContainer {public static void main(String[] args) {ServerEndpointConfig serverConfig = ServerEndpointConfig.Builder.create(WebSocketServer.class, "/websocket").build();WebSocketContainer container = ContainerProvider.getWebSocketContainer();try {container.connectToServer(serverConfig, new URI("ws://localhost:8080/"));} catch (Exception e) {e.printStackTrace();}}
}
注:這里監聽的地址不可以是 "ws" 不然會報錯,可能這是關鍵字吧,畢竟我們的協議就叫 ws?
2.1.2、Vue + JS 實現客戶端
<template><div class="app-container home"><el-row :gutter="20"><el-col :sm="24" :lg="24"><h1>集成websocket測試</h1></el-col></el-row><el-row :gutter="20"><el-col :sm="24" :lg="24"><div><el-input v-model="url" type="text" style="width: 20%" /> <el-button @click="join" type="primary">連接</el-button><el-button @click="exit" type="danger">斷開</el-button><el-button @click="resetForm" type="success">重置</el-button><br /><br /><el-input type="textarea" v-model="message" :rows="9" /><br /><br /><el-button type="success" @click="send">發送消息</el-button><br /><br />返回內容<el-input type="textarea" v-model="text_content" :rows="9" /><br /><br /></div></el-col></el-row></div>
</template><script>
import { getToken } from "@/utils/auth";export default {name: "Index",data() {return {url: "ws://127.0.0.1:8080/websocket/message",message: "",text_content: "",ws: null,headers: {Authorization: "Bearer " + getToken(),},};},methods: {join() {const wsuri = this.url;// this.ws = new WebSocket(wsuri);this.ws = new WebSocket(wsuri, [getToken()]);const self = this;// 連接成功后調用this.ws.onopen = function (event) {self.text_content = self.text_content + "WebSocket連接成功!" + "\n";};this.ws.onerror = function (event) {self.text_content = self.text_content + "WebSocket連接發生錯誤!" + "\n";};// 接收后端消息this.ws.onmessage = function (event) {self.text_content = self.text_content + event.data + "\n";};// 關閉連接時調用this.ws.onclose = function (event) {self.text_content = self.text_content + "已經關閉連接!" + "\n";};},exit() {if (this.ws) {this.ws.close();this.ws = null;}},send() {if (this.ws) {this.ws.send(this.message);} else {alert("未連接到服務器");}},//重置resetForm() {this.message = "";this.text_content = "";},},
};
</script>
2.2、Spring Boot集成WebSocket
2.2.1、添加依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.2.2、配置WebSocket
應用程序中,需要配置WebSocket。創建一個新的Java類,并添加注。@ServerEndpoint(“/websocket”)。這將指定WebSocket服務端的端點。
在此類中,需要實現幾個方法:
import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;@ServerEndpoint("/websocket")
public class WebSocketServer {@OnOpenpublic void onOpen(Session session) {System.out.println("Connection opened: " + session.getId());sessions.add(session);}@OnMessagepublic void onMessage(Session session, String message) throws IOException {System.out.println("Received message: " + message);session.getBasicRemote().sendText("Server received: " + message);}@OnClosepublic void onClose(Session session) {System.out.println("Connection closed: " + session.getId());sessions.remove(session);}private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
}
2.2.3、處理WebSocket消息
在@OnMessage方法中,可以處理WebSocket客戶端發送的消息,并向客戶端發送響應。下面是一個簡單的示例代碼:
@OnMessage
public void onMessage(Session session, String message) throws IOException {System.out.println("Received message: " + message);session.getBasicRemote().sendText("Server received: " + message);
}
在此代碼中,我們簡單地打印出收到的消息,并向客戶端發送響應。
關閉WebSocket連接
在@OnClose方法中,可以刪除連接并做一些清理工作。下面是一個示例代碼:
@OnClose
public void onClose(Session session) {System.out.println("Connection closed: " + session.getId());sessions.remove(session);
}
在此代碼中,我們從連接池中刪除連接,并打印出連接已關閉的消息。
配置WebSocket支持
最后,需要配置Spring Boot以支持WebSocket。創建一個新的Java類,并添加注釋@Configuration和@EnableWebSocket。然后,需要覆蓋方法registerWebSocketHandlers(),并指定WebSocket處理程序。下面是一個示例代碼:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");}
}
在此代碼中,我們創建了一個新的WebSocketServer對象,并將其添加到WebSocket處理程序中。我們還指定了WebSocket端點(/websocket)和允許的來源(*)。
三、PostMan調用
3.1、Websocket在線模擬請求工具
訪問訪問地址:http://www.jsons.cn/websocket/
具有進行連接、斷開、模擬發送數據等功能。
(請求時注意連接格式為 ws://IP或域名:端口(示例 ws://127.0.0.1:8089/websocket/devices)
3.2、Postman
使用新版的Postman
1、建立 WebSocket 連接
在 File–> New 頁面,選擇 WebSocket Request,即可創建一個 WebSocket 模擬請求。
2、模擬數據交互
在地址欄中輸入相應的 WebSocket 請求地址,點擊地址欄右側的 “Connect” 按鈕,即可建立連接。
連接建立成功后,在 Message 的信息欄中輸入模擬數據,點擊 “Send” 按鈕,即可與服務端進行數據交互。
優勢:
支持多種數據格式
如:Text、JSON、XML、HTML、Binary等;
支持對交互信息進行格式化顯示
如:Text、JSON、XML、HTML等;
支持對交互數據進行模糊搜索、篩選過濾、清空等操作;
交互數據按照時間倒序顯示,更便于查看最新的數據。
3、斷開 WebSocket 連接
如果調試結束,點擊地址欄右側的 “Disconnect” 按鈕,即可斷開與 WebSocket 服務端的連接
可參考
WebSocket詳解及使用教程:打造高效的實時通信(二)_利用websocket進行聊天-CSDN博客
Java中使用WebSocket的幾種方式_java_腳本之家