springboot整合websocket(一)
東西太多了,拆成幾章來寫(絕對不是騙流量^ w ^)
這一部分就簡單做一個公共聊天室吧
1、引入相關依賴
springboot相關依賴就不寫了,這里只寫websocket的依賴
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、寫組態檔
這里注意不要漏了 @EnableWebSocket,用于開啟websocket支持,同時 @Configuration將配置類注入spring容器
package com.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
//開啟websocket支持
@EnableWebSocket
public class WebsocketConfig
{
/**
* 必須要有的
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
/**
* websocket 配置資訊
*
* @return
*/
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer()
{
ServletServerContainerFactoryBean bean = new ServletServerContainerFactoryBean();
//文本緩沖區大小
bean.setMaxTextMessageBufferSize(8192);
//位元組緩沖區大小
bean.setMaxBinaryMessageBufferSize(8192);
return bean;
}
}
3、開始愉快的使用啦
這里先介紹幾個websocket的注解
| 注解 | 作用 | 備注 |
|---|---|---|
| @ServerEndpoint | 用于宣告websocket回應類,有點像@RequestMapping | @ServerEndpoint("/websocket") |
| @OnOpen | websocket連接時觸發 | 引數有:Session session, EndpointConfig config |
| @OnMessage | 有訊息時觸發 | 引數很多,一會再說 |
| @OnClose | 連接關閉時觸發 | 引數有:Session session, CloseReason closeReason |
| @OnError | 有例外時觸發 | 引數有:Session session, Throwable throwable |
3.1(重要) 將@ServerEndpoint標注在類上,然后依次創建4個方法,引數見上表,方法名隨意
注意類上需要有 @Component 掃描哦,我這里用的時@Controller
@Log4j2
@Controller
@ServerEndpoint("/websocket")
public class BaseWebsocketController
{
//使用 ConcurrentHashMap, 保證執行緒安全, static全域共享 session
//這里之所以static,是因為這個類不是單例的!!
//他雖然有@Controller注解,但是不適用Ioc容器中拿物件,每一次請求過來都是一個新的物件
//存放 session
private final static Map<String, Session> sessions = new ConcurrentHashMap<>();
//onopen 在連接創建(用戶進入聊天室)時觸發
@OnOpen
public void openSession(Session session, EndpointConfig config)
{
}
//回應字串
@OnMessage
public void onMessage(Session session, String message)
{
}
//回應位元組流
@OnMessage
public void onMessage(Session session, byte[] message)
{
}
//onclose 在連接斷開(用戶離開聊天室)時觸發
@OnClose
public void closeSession(Session session, CloseReason closeReason)
{
}
@OnError
public void sessionError(Session session, Throwable throwable)
{
}
}
說明
細心的小伙伴可能發現,我有兩個 @OnMessage, 這是因為websocket能發送三種請求(我知道的三種),一種是字串,一種是位元組流(用于上傳檔案),一種是ping-pong(乒乓機制),因為js不好發送ping請求,我這里就只有回應字串和位元組流兩種方法,
接下來的篇幅將只演示字串的,位元組流咱再另一篇說,不然太多了看的頭痛
3.2 往方法里面寫點簡單的東西
里面注釋寫個很清楚哦,慢慢瞅,咱就解釋一下流程:
1、再OnOpen中將session存起來,并通知其他用戶,有人來啦,
2、有訊息來的時候,再OnMessage中,通知其他用戶
3、OnClose中,通知其他用戶,別人溜了
4、OnError中,有例外就關閉websocket
@Log4j2
@Controller
@ServerEndpoint("/websocket")
public class BaseWebsocketController
{
//使用 ConcurrentHashMap, 保證執行緒安全, static全域共享 session
//這里之所以static,是因為這個類不是單例的!!
//他雖然有@Controller注解,但是不適用Ioc容器中拿物件,每一次請求過來都是一個新的物件
//存放 session
private final static Map<String, Session> sessions = new ConcurrentHashMap<>();
//onopen 在連接創建(用戶進入聊天室)時觸發
@OnOpen
public void openSession(Session session, EndpointConfig config)
{
//將session存起來, 用于服務器向瀏覽器發送訊息
sessions.put(session.getId(), session);
sendAll("[" + session.getId() + "]進入房間");
}
//回應字串
@OnMessage
public void onMessage(Session session, String message)
{
sendAll("[" + session.getId() + "]" + message);
}
//回應位元組流
@OnMessage
public void onMessage(Session session, byte[] message)
{
//這個咱以后再說
}
//onclose 在連接斷開(用戶離開聊天室)時觸發
@OnClose
public void closeSession(Session session, CloseReason closeReason)
{
//記得移除相對應的session
sessions.remove(session.getId());
sendAll("[" + session.getId() + "]離開了房間");
}
@OnError
public void sessionError(Session session, Throwable throwable)
{
//通常有例外會關閉session
try {
session.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
private void sendAll(String message)
{
for (Session s : sessions.values()) {
//獲得session發送訊息的物件
//Basic是同步, 會阻塞
//Async是異步, 這個會有多執行緒并發導致例外, 發送訊息太快也會有并發例外, 需要有 訊息佇列 來輔助使用
final RemoteEndpoint.Basic remote = s.getBasicRemote();
try {
//發送訊息
remote.sendText(message);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.3 寫個頁面使用websocket
先上效果圖

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>websocket-demo</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css">
</head>
<body>
<div class="container py-3">
<div class="row">
<div class="col-6">
<div>
<label for="messageArea">聊天資訊:</label>
</div>
<div>
<textarea id="messageArea" readonly class="w-100" style="height: 75vh;"></textarea>
</div>
</div>
<div class="col">
<div class="my-1">
<label for="messageArea">用 戶 名:</label>
</div>
<div class="my-1">
<input type="text" id="username" autocomplete="off">
</div>
<div class="my-1">
<button class="btn-info" id="joinRoomBtn">進入聊天室</button>
<button class="btn-warning" id="leaveRoomBtn">離開聊天室</button>
</div>
<hr/>
<div class="my-1">
<label for="sendMessage">輸入訊息:</label>
</div>
<div>
<textarea id="sendMessage" rows="5" class="w-100" style="max-height: 50vh"></textarea>
</div>
<div class="my-1">
<button class="btn-primary" id="sendBtn">發送訊息</button>
</div>
</div>
</div>
</div>
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
<script>
let webSocket;
//ip和埠號用自己專案的
//{websocket}: 其實是剛剛那個@ServerEndpoint("/websocket")中定義的
let url = 'ws://127.0.0.1:8080/websocket';
$('#username').keyup(function (e) {
let keycode = e.which;
if (keycode == 13) {
$('#joinRoomBtn').click();
}
});
//進入聊天室
$('#joinRoomBtn').click(function () {
let username = $('#username').val();
webSocket = new WebSocket(url);
webSocket.onopen = function () {
console.log('webSocket連接創建,,,');
}
webSocket.onclose = function () {
console.log('webSocket已斷開,,,');
$('#messageArea').append('websocket已斷開\n');
}
webSocket.onmessage = function (event) {
$('#messageArea').append(event.data + '\n');
}
webSocket.onerror = function (event) {
console.log(event)
console.log('webSocket連接例外,,,');
}
});
//退出聊天室
$('#leaveRoomBtn').click(function () {
if (webSocket) {
//關閉連接
webSocket.close();
}
});
//發送訊息
$('#sendBtn').click(function () {
var msg = $('#sendMessage').val();
if (msg.trim().length === 0) {
alert('請輸入內容');
return;
}
webSocket.send($('#sendMessage').val());
$('#sendMessage').val('');
});
</script>
</body>
</html>
4、最后填下坑
4.1@OnOpen、@OnMessage…那些引數是咋來滴?
當然是看注釋啦!轉到原始碼,再類的上邊有一大串注釋,百度翻譯看下就行,
我上邊的引數還有漏的哦~~太多了解釋不過來,
4.2 里面有一段是這個
and Zero to n String or Java primitive parameters
annotated with the {@link javax.websocket.server.PathParam} annotation for server endpoints.
意思是可以再路徑中加入引數,然后用 @PathParam注解獲得該引數(是不是和web很像呢)
也就是說,可以這樣
@ServerEndpoint("/websocket/{username}")
然后這樣獲得引數(所有方法都可以加),而且可以不止一個
//回應字串
@OnMessage
public void onMessage(@PathParam("username") String username, Session session, String message)
{
sendAll("[" + username + "]" + message);
}
再html連接時,url就這么寫
let username = $('#username').val();
let url = 'ws://127.0.0.1:8080/websocket';
url = url+'/'+username;
所以啊,剛剛我們demo的用戶名是可以顯示出來的(這個就交給大家了,懶 >_<),
End
以后有時間寫下關于websocket檔案上傳的,著急的小伙伴下面評論,我開個小灶丫~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/353326.html
標籤:其他
