SpringBoot+WebSocket
- 一、WebSocket介紹
- 二、WebSocket運行機制
- 三、WebSocket實作
一、WebSocket介紹
網站上的即時通訊是很常見的,比如網頁的QQ,聊天系統等,按照以往的技術能力通常是采用輪詢、Comet技術解決,
HTTP協議是非持久化的,單向的網路協議,在建立連接后只允許瀏覽器向服務器發出請求后,服務器才能回傳相應的資料,當需要即時通訊時,通過輪詢在特定的時間間隔(如1秒),由瀏覽器向服務器發送Request請求,然后將最新的資料回傳給瀏覽器,這樣的方法最明顯的缺點就是需要不斷的發送請求,而且通常HTTP request的Header是非常長的,為了傳輸一個很小的資料 需要付出巨大的代價,是很不合算的,占用了很多的寬帶,
缺點:會導致過多不必要的請求,浪費流量和服務器資源,每一次請求、應答,都浪費了一定流量在相同的頭部資訊上
然而WebSocket的出現可以彌補這一缺點,在WebSocket中,只需要服務器和瀏覽器通過HTTP協議進行一個握手的動作,然后單獨建立一條TCP的通信通道進行資料的傳送,
二、WebSocket運行機制
WebSocket 是 HTML5 一種新的協議,也是一個典型的應用層協議,它實作了瀏覽器與服務器全雙工通信,能更好的節省服務器資源和帶寬并達到實時通訊,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸資料,但是它和 HTTP 最大不同是:
WebSocket 是一種雙向通信協議,在建立連接后,WebSocket 服務器和 Browser/Client Agent 都能主動的向對方發送或接收資料,就像 Socket 一樣;
WebSocket 需要類似 TCP 的客戶端和服務器端通過握手連接,連接成功后才能相互通信,
非 WebSocket 模式傳統 HTTP 客戶端與服務器的互動如下圖所示:

使用 WebSocket 模式客戶端與服務器的互動如下圖:

三、WebSocket實作
這里通過一個簡單的聊天例程,實作spring boot +websocket代碼的演示,
引入maven
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
配置類WebConfig
package org.antry.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @ClassName WebSocketConfig
* @Description TODO
* @Autor TT
* @Date 2020/10/9 23:00
* @Version 1.0
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocket類
在這個類里,有一個集合,用來存放所有連接,
當有新的連接進來時,在onOpen()方法中,我們每個連接都會存下對應id作為標識,假設這個id已經存過,則會關閉此連接,防止同一個id多處登錄,
連接斷開時,在onClose()方法中,將此連接移出集合,
當有新的訊息發送過來時,會去遍歷連接,找到對應接收人id的連接,然后把訊息推送給接收人,
package org.antry.websocket;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName MyWebsocket
* @Description TODO
* @Autor TT
* @Date 2020/10/10 11:02
* @Version 1.0
*/
@ServerEndpoint(value = "/websocket/{user}")
@Component
public class MyWebsocket {
// 通過類似GET請求方式傳遞引數的方法(服務端采用第二種方法"WebSocketHandler"實作)
// websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");
/**
* 在線人數
*/
public static AtomicInteger onlineNumber = new AtomicInteger(0);
/**
* 所有的物件,每次連接建立,都會將我們自己定義的MyWebsocket存放到List中,
*/
public static List<MyWebsocket> webSockets = new CopyOnWriteArrayList<MyWebsocket>();
/**
* 會話,與某個客戶端的連接會話,需要通過它來給客戶端發送資料
*/
private Session session;
/**
* 每個會話的用戶
*/
private String user;
/**
* 建立連接
*
* @param session
*/
@OnOpen
public void onOpen(Session session, @PathParam("user") String user) {
System.err.println(user);
if (user == null || "".equals(user)) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
onlineNumber.incrementAndGet();
for (MyWebsocket MyWebsocket : webSockets) {
if (user.equals(MyWebsocket.user)) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
}
this.session = session;
this.user = user;
webSockets.add(this);
}
/**
* 連接關閉
*/
@OnClose
public void onClose() {
onlineNumber.decrementAndGet();
webSockets.remove(this);
}
/**
* 收到客戶端的訊息
*
* @param message 訊息
* @param session 會話
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("user") String user) {
System.err.println(message);
String[] strArr = message.split("~");
pushMessage(user,strArr[0],strArr[1]);
}
/**
* 發送訊息
*
* @param message 訊息
*/
public void sendMessage(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 訊息推送
*
* @param message
* @param uuid uuid為空則推送全部人員
*/
public static void pushMessage(String user, String message, String uuid) {
if (uuid == null || "".equals(uuid)) {
for (MyWebsocket MyWebsocket : webSockets) {
MyWebsocket.sendMessage(user + ":" + message);
}
} else {
for (MyWebsocket MyWebsocket : webSockets) {
if (uuid.equals(MyWebsocket.user)) {
MyWebsocket.sendMessage(message);
}
}
}
}
}
兩個簡單的前端頁面,他們的不同是,接收id和發送id不同,
testOne.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天視窗1</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="public/dist/lib/uploader/zui.uploader.min.css" rel="stylesheet">
</head>
<body>
<div>
<div class="input-group">
<input type="text" class="form-control" id="msg">
<span class="input-group-btn">
<button class="btn btn-default" type="button" onclick="send()">發送</button>
</span>
</div>
</div>
<script src="public/dist/lib/jquery/jquery.js"></script>
<script src="public/dist/js/zui.min.js"></script>
<script src="public/layer/layer.js"></script>
<script src="public/js/function.js"></script>
<script src="public/js/testOne.js"></script>
</body>
</html>
testOne.js
var websocket = null;
var sendId = 0;
var receiveId = 1;
//---------------------------
/**
* 初始化websocket
* @param id
*/
doInit(sendId)
function doInit(sendId){
if ('WebSocket' in window) {
websocket = new WebSocket("ws:localhost:10086/websocket/" + sendId);
} else {
alert("瀏覽器不支持");
}
websocket.onopen = function () {
addMessage("webscoket已經連接成功");
};
websocket.onclose = function () {
addMessage("webscoket連接失敗");
};
websocket.onmessage = function (event) {
alert("收到訊息:"+event.data);
};
websocket.onerror = function () {
addMessage("webscoket連接失敗");
};
}
/**
* 發送一條訊息
*/
function send(){
websocket.send(value('msg')+"~"+receiveId);
}
/**
* 測驗列印除錯資訊用
* @param msg
*/
function addMessage(msg) {
console.log(msg)
}
testTwo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天視窗2</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="public/dist/lib/uploader/zui.uploader.min.css" rel="stylesheet">
</head>
<body>
<div>
<div class="input-group">
<input type="text" class="form-control" id="msg">
<span class="input-group-btn">
<button class="btn btn-default" type="button" onclick="send()">發送</button>
</span>
</div>
</div>
<script src="public/dist/lib/jquery/jquery.js"></script>
<script src="public/dist/js/zui.min.js"></script>
<script src="public/layer/layer.js"></script>
<script src="public/js/function.js"></script>
<script src="public/js/testTwo.js"></script>
</body>
</html>
testTwo.js
var websocket = null;
var sendId = 1;
var receiveId = 0;
//---------------------------
/**
* 初始化websocket
* @param id
*/
doInit(sendId)
function doInit(receiveId){
if ('WebSocket' in window) {
websocket = new WebSocket("ws:localhost:10086/websocket/" + sendId);
} else {
alert("瀏覽器不支持");
}
websocket.onopen = function () {
addMessage("webscoket已經連接成功");
};
websocket.onclose = function () {
addMessage("webscoket連接失敗");
};
websocket.onmessage = function (event) {
alert("收到訊息:"+event.data);
};
websocket.onerror = function () {
addMessage("webscoket連接失敗");
};
}
/**
* 發送一條訊息
*/
function send(){
websocket.send(value('msg')+"~"+receiveId);
}
/**
* 測驗列印除錯資訊用
* @param msg
*/
function addMessage(msg) {
console.log(msg)
}
分別用兩個瀏覽器,打開這兩個頁面進行訪問,結果如下


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/195266.html
標籤:其他
