新版springboot2.x websocket實現
阿新 • • 發佈:2021-01-25
網上有很多關於WebSocket用法的文章,但是有一些內容錯誤或者程式碼不全的,這裡來個全的,方便自己記憶。
前言:
WebSocket是一種在單個TCP連線上進行全雙工通訊的協議。WebSocket通訊協議於2011年被IETF定為標準RFC 6455,並由RFC7936補充規範。WebSocketAPI也被W3C定為標準。WebSocket使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。
說明:
本篇主要介紹在SpringBoot框架下,WebSocket基於註解使用的3種場景:
1、自己給自己發訊息
2、自己給所有客戶端傳送訊息(不包括自己)
3、自己給另一個客戶端傳送訊息
工程目錄:
pom檔案中的依賴:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springboot-7-websocket</artifactId> <description> websocket learn...</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <!-- websocket --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.yzgu.</groupId> <artifactId>common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
@Configuration
public class WebSocketConfig {
/**
* 注入一個ServerEndpointExporter,該Bean會自動註冊使用@ServerEndpoint註解申明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
@SpringBootApplication public class AppApplication { public static void main(String[] args) { SpringApplication.run(AppApplication.class, args); } }
public class MyMessage {
private String userId;
private String message;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "{" +
"userId='" + userId + '\'' +
", message='" + message + '\'' +
'}';
}
}
package com.yzgu.up.component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* 前後端互動的類實現訊息的接收推送(自己傳送給所有人(不包括自己))
*
* @ServerEndpoint(value = "/test/oneToMany") 前端通過此URI 和後端互動,建立連線
*/
@Slf4j
@ServerEndpoint(value = "/test/oneToMany")
@Component
public class OneToManyWebSocket {
/** 記錄當前線上連線數 */
private static AtomicInteger onlineCount = new AtomicInteger(0);
/** 存放所有線上的客戶端 */
private static Map<String, Session> clients = new ConcurrentHashMap<>();
/**
* 連線建立成功呼叫的方法
*/
@OnOpen
public void onOpen(Session session) {
onlineCount.incrementAndGet(); // 線上數加1
clients.put(session.getId(), session);
log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose(Session session) {
onlineCount.decrementAndGet(); // 線上數減1
clients.remove(session.getId());
log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message
* 客戶端傳送過來的訊息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("服務端收到客戶端[{}]的訊息:{}", session.getId(), message);
this.sendMessage(message, session);
}
@OnError
public void onError(Session session, Throwable error) {
log.error("發生錯誤");
error.printStackTrace();
}
/**
* 群發訊息
*
* @param message
* 訊息內容
*/
private void sendMessage(String message, Session fromSession) {
for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
Session toSession = sessionEntry.getValue();
// 排除掉自己
if (!fromSession.getId().equals(toSession.getId())) {
log.info("服務端給客戶端[{}]傳送訊息{}", toSession.getId(), message);
toSession.getAsyncRemote().sendText(message);
}
}
}}
package com.yzgu.up.component;
import com.alibaba.fastjson.JSON;
import com.yzgu.up.MyMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 前後端互動的類實現訊息的接收推送(自己傳送給另一個人)
*
* @ServerEndpoint(value = "/test/oneToOne") 前端通過此URI 和後端互動,建立連線
*/
@Slf4j
@ServerEndpoint(value = "/test/oneToOne")
@Component
public class OneToOneWebSocket {
/** 記錄當前線上連線數 */
private static AtomicInteger onlineCount = new AtomicInteger(0);
/** 存放所有線上的客戶端 */
private static Map<String, Session> clients = new ConcurrentHashMap<>();
/**
* 連線建立成功呼叫的方法
*/
@OnOpen
public void onOpen(Session session) {
onlineCount.incrementAndGet(); // 線上數加1
clients.put(session.getId(), session);
log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose(Session session) {
onlineCount.decrementAndGet(); // 線上數減1
clients.remove(session.getId());
log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message
* 客戶端傳送過來的訊息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("服務端收到客戶端[{}]的訊息[{}]", session.getId(), message);
try {
// 測試時 傳送的訊息格式: {"message":"ss222","userId":"1"}
MyMessage myMessage = JSON.parseObject(message, MyMessage.class);
if (myMessage != null) {
Session toSession = clients.get(myMessage.getUserId());
if (toSession != null) {
this.sendMessage(myMessage.getMessage(), toSession);
}
}
} catch (Exception e) {
log.error("解析失敗:{}", e);
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("發生錯誤");
error.printStackTrace();
}
/**
* 服務端傳送訊息給客戶端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服務端給客戶端[{}]傳送訊息[{}]", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服務端傳送訊息給客戶端失敗:{}", e);
}
}
}
package com.yzgu.up.component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 前後端互動的類實現訊息的接收推送(自己傳送給自己)
*
* @ServerEndpoint(value = "/test/one") 前端通過此URI和後端互動,建立連線
*/
@Slf4j
@ServerEndpoint(value = "/test/one")
@Component
public class OneWebSocket {
/**
* 記錄當前線上連線數
*/
private static AtomicInteger onlineCount = new AtomicInteger(0);
/**
* 連線建立成功呼叫的方法
*/
@OnOpen
public void onOpen(Session session) {
onlineCount.incrementAndGet(); // 線上數加1
log.info("有新連線加入:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose(Session session) {
onlineCount.decrementAndGet(); // 線上數減1
log.info("有一連線關閉:{},當前線上人數為:{}", session.getId(), onlineCount.get());
}
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message 客戶端傳送過來的訊息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("服務端收到客戶端[{}]的訊息:{}", session.getId(), message);
this.sendMessage("Hello, " + message, session);
}
@OnError
public void onError(Session session, Throwable error) {
log.error("發生錯誤");
error.printStackTrace();
}
/**
* 服務端傳送訊息給客戶端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服務端給客戶端[{}]傳送訊息{}", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服務端傳送訊息給客戶端失敗:{}", e);
}
}
}
<!DOCTYPE HTML>
<html>
<head>
<title>My WebSocket</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判斷當前瀏覽器是否支援WebSocket, 主要此處要更換為自己的地址
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:18092/test/oneToOne");
} else {
alert('Not support websocket')
}
//連線發生錯誤的回撥方法
websocket.onerror = function() {
setMessageInnerHTML("error");
};
//連線成功建立的回撥方法
websocket.onopen = function(event) {
//setMessageInnerHTML("open");
}
//接收到訊息的回撥方法
websocket.onmessage = function(event) {
setMessageInnerHTML(event.data);
}
//連線關閉的回撥方法
websocket.onclose = function() {
setMessageInnerHTML("close");
}
//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。
window.onbeforeunload = function() {
websocket.close();
}
//將訊息顯示在網頁上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關閉連線
function closeWebSocket() {
websocket.close();
}
//傳送訊息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
server:
port: 18092
個人比較懶,程式碼都貼了,你們注意下埠和地址對應 ,基本上就ok了。
直接訪問 index 測試就好了