使用WebSocket+SpringBoot+Vue搭建簡易網(wǎng)頁聊天室的實(shí)現(xiàn)代碼
一、數(shù)據(jù)庫搭建
很簡單的一個(gè)user表,加兩個(gè)用戶admin和wskh

二、后端搭建
2.1 引入關(guān)鍵依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.2 WebSocket配置類
WebSocketConfig的作用是:開啟WebSocket監(jiān)聽
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Author:WSKH
* @ClassName:WebSocketConfig
* @ClassType:配置類
* @Description:WebSocket配置類
* @Date:2022/1/25/12:21
* @Email:1187560563@qq.com
* @Blog:https://blog.csdn.net/weixin_51545953?type=blog
*/
@Configuration
public class WebSocketConfig {
/** * 開啟webSocket * @return */
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}WebSocketServer里寫了一些事件,如發(fā)送消息事件,建立連接事件,關(guān)閉連接事件等
import com.wskh.chatroom.util.FastJsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.EOFException;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
private static int onlineCount = 0;
private static ConcurrentHashMap<String,WebSocketServer> webSocketServerMap = new ConcurrentHashMap<>();
private Session session;
private String sid;
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
this.sid = sid;
this.session = session;
webSocketServerMap.put(sid, this);
addOnlineCount();
log.info("有新窗口開始監(jiān)聽:"+sid+",當(dāng)前在線人數(shù)為" + getOnlineCount());
try {
sendInfo("openSuccess:"+webSocketServerMap.keySet());
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose() {
webSocketServerMap.remove(sid);
subOnlineCount();
log.info("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
try {
sendInfo("openSuccess:"+webSocketServerMap.keySet());
} catch (IOException e) {
e.printStackTrace();
}
}
@OnMessage
public void onMessage(String message) throws IOException {
if("ping".equals(message)) {
sendInfo(sid, "pong");
}
if(message.contains(":")) {
String[] split = message.split(":");
sendInfo(split[0], "receivedMessage:"+sid+":"+split[1]);
}
}
@OnError
public void onError(Session session, Throwable error) {
if(error instanceof EOFException) {
return;
}
if(error instanceof IOException && error.getMessage().contains("已建立的連接")) {
return;
}
log.error("發(fā)生錯(cuò)誤", error);
}
/**
* 實(shí)現(xiàn)服務(wù)器主動(dòng)推送
*/
public void sendMessage(String message) throws IOException {
synchronized (session) {
this.session.getBasicRemote().sendText(message);
}
}
public static void sendObject(Object obj) throws IOException {
sendInfo(FastJsonUtils.convertObjectToJSON(obj));
}
public static void sendInfo(String sid,String message) throws IOException {
WebSocketServer socketServer = webSocketServerMap.get(sid);
if(socketServer != null) {
socketServer.sendMessage(message);
}
}
public static void sendInfo(String message) throws IOException {
for(String sid : webSocketServerMap.keySet()) {
webSocketServerMap.get(sid).sendMessage(message);
}
}
public static void sendInfoByUserId(Long userId,Object message) throws IOException {
for(String sid : webSocketServerMap.keySet()) {
String[] sids = sid.split("id");
if(sids.length == 2) {
String id = sids[1];
if(userId.equals(Long.parseLong(id))) {
webSocketServerMap.get(sid).sendMessage(FastJsonUtils.convertObjectToJSON(message));
}
}
}
}
public static Session getWebSocketSession(String sid) {
if(webSocketServerMap.containsKey(sid)) {
return webSocketServerMap.get(sid).session;
}
return null;
}
public static synchronized void addOnlineCount() {
onlineCount++;
}
public static synchronized void subOnlineCount() {
onlineCount--;
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
}
2.3 配置跨域
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
// 跨域配置
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}
2.4 發(fā)送消息的控制類
/**
* @Author:WSKH
* @ClassName:MsgController
* @ClassType:控制類
* @Description:信息控制類
* @Date:2022/1/25/12:47
* @Email:1187560563@qq.com
* @Blog:https://blog.csdn.net/weixin_51545953?type=blog
*/
@ApiModel("信息控制類")
@RestController
@RequestMapping("/chatroom/msg")
public class MsgController {
@ApiOperation("發(fā)送信息方法")
@PostMapping("/sendMsg")
public R sendMsg(String msg) throws IOException {
WebSocketServer.sendInfo(msg);
return R.ok().message("發(fā)送成功");
}
}
至此,后端部分大體配置完畢。
三、前端搭建
本文使用vue-admin-template-master模板進(jìn)行聊天室的前端搭建
3.1 自定義文件websocket.js
將下面文件放在api文件夾下

//websocket.js
import Vue from 'vue'
// 1、用于保存WebSocket 實(shí)例對(duì)象
export const WebSocketHandle = undefined
// 2、外部根據(jù)具體登錄地址實(shí)例化WebSocket 然后回傳保存WebSocket
export const WebsocketINI = function(websocketinstance) {
this.WebSocketHandle = websocketinstance
this.WebSocketHandle.onmessage = OnMessage
}
// 3、為實(shí)例化的WebSocket綁定消息接收事件:同時(shí)用于回調(diào)外部各個(gè)vue頁面綁定的消息事件
// 主要使用WebSocket.WebSocketOnMsgEvent_CallBack才能訪問 this.WebSocketOnMsgEvent_CallBack 無法訪問很詭異
const OnMessage = function(msg) {
// 1、消息打印
// console.log('收到消息:', msg)
// 2、如果外部回調(diào)函數(shù)未綁定 結(jié)束操作
if (!WebSocket.WebSocketOnMsgEvent_CallBack) {
console.log(WebSocket.WebSocketOnMsgEvent_CallBack)
return
}
// 3、調(diào)用外部函數(shù)
WebSocket.WebSocketOnMsgEvent_CallBack(msg)
}
// 4、全局存放外部頁面綁定onmessage消息回調(diào)函數(shù):注意使用的是var
export const WebSocketOnMsgEvent_CallBack = undefined
// 5、外部通過此綁定方法 來傳入的onmessage消息回調(diào)函數(shù)
export const WebSocketBandMsgReceivedEvent = function(receiveevent) {
WebSocket.WebSocketOnMsgEvent_CallBack = receiveevent
}
// 6、封裝一個(gè)直接發(fā)送消息的方法:
export const Send = function(msg) {
if (!this.WebSocketHandle || this.WebSocketHandle.readyState !== 1) {
// 未創(chuàng)建連接 或者連接斷開 無法發(fā)送消息
return
}
this.WebSocketHandle.send(msg)// 發(fā)送消息
}
// 7、導(dǎo)出配置
const WebSocket = {
WebSocketHandle,
WebsocketINI,
WebSocketBandMsgReceivedEvent,
Send,
WebSocketOnMsgEvent_CallBack
}
// 8、全局綁定WebSocket
Vue.prototype.$WebSocket = WebSocket
3.2 main.js中全局引入websocket
import '@/utils/websocket' // 全局引入 WebSocket 通訊組件
3.3 App.vue中聲明websocket對(duì)象
App.vue
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
import {getInfo} from './api/login.js';
import {getToken} from './utils/auth.js'
export default {
name: 'App',
mounted() {
// 每3秒檢測一次websocket連接狀態(tài) 未連接 則嘗試連接 盡量保證網(wǎng)站啟動(dòng)的時(shí)候 WebSocket都能正常長連接
setInterval(this.WebSocket_StatusCheck, 3000)
// 綁定消息回調(diào)事件
this.$WebSocket.WebSocketBandMsgReceivedEvent(this.WebSocket_OnMesage)
// 初始化當(dāng)前用戶信息
this.token = getToken()
getInfo(this.token).then((rep)=>{
console.log(rep)
this.userName = rep.data.name
}).catch((error)=>{
console.log(error)
})
},
data(){
return{
}
},
methods: {
// 實(shí)際消息回調(diào)事件
WebSocket_OnMesage(msg) {
console.log('收到服務(wù)器消息:', msg.data)
console.log(msg)
let chatDiv = document.getElementById("chatDiv")
let newH3 = document.createElement("div")
if(msg.data.indexOf('openSuccess')>=0){
// 忽略連接成功消息提示
}else{
if(msg.data.indexOf(this.userName)==0){
// 說明是自己發(fā)的消息,應(yīng)該靠右邊懸浮
newH3.innerHTML = "<div style='width:100%;text-align: right;'><h3 style=''>"+msg.data+"</h3></div>"
}else{
newH3.innerHTML = "<div style='width:100%;text-align: left;'><h3 style=''>"+msg.data+"</h3></div>"
}
}
chatDiv.appendChild(newH3)
},
// 1、WebSocket連接狀態(tài)檢測:
WebSocket_StatusCheck() {
if (!this.$WebSocket.WebSocketHandle || this.$WebSocket.WebSocketHandle.readyState !== 1) {
console.log('Websocket連接中斷,嘗試重新連接:')
this.WebSocketINI()
}
},
// 2、WebSocket初始化:
async WebSocketINI() {
// 1、瀏覽器是否支持WebSocket檢測
if (!('WebSocket' in window)) {
console.log('您的瀏覽器不支持WebSocket!')
return
}
let DEFAULT_URL = "ws://" + '127.0.0.1:8002' + '/websocket/' + new Date().getTime()
// 3、創(chuàng)建Websocket連接
const tmpWebsocket = new WebSocket(DEFAULT_URL)
// 4、全局保存WebSocket操作句柄:main.js 全局引用
this.$WebSocket.WebsocketINI(tmpWebsocket)
// 5、WebSocket連接成功提示
tmpWebsocket.onopen = function(e) {
console.log('webcoket連接成功')
}
//6、連接失敗提示
tmpWebsocket.onclose = function(e) {
console.log('webcoket連接關(guān)閉:', e)
}
}
}
}
</script>
3.4 聊天室界面.vue
<template>
<div>
<div style="margin-top: 1vh;margin-bottom: 1vh;font-weight: bold;">聊天內(nèi)容:</div>
<div style="background-color: #c8c8c8;width: 100%;height: 80vh;" id="chatDiv">
</div>
<div style="margin-top: 1vh;margin-bottom: 1vh;font-weight: bold;">聊天輸入框:</div>
<el-input v-model="text">
</el-input>
<el-button @click="sendMsg">點(diǎn)擊發(fā)送</el-button>
</div>
</template>
<script>
import {getInfo} from '../../api/login.js';
import {getToken} from '../../utils/auth.js'
import msgApi from '../../api/msg.js'
export default {
mounted() {
//
this.token = getToken()
getInfo(this.token).then((rep)=>{
console.log(rep)
this.userName = rep.data.name
}).catch((error)=>{
console.log(error)
})
},
data() {
return {
text: "",
token:"",
userName:"",
}
},
methods: {
sendMsg(){
let msg = this.userName+":"+this.text
msgApi.sendMsg(msg).then((rep)=>{
}).catch((error)=>{
})
this.text = ""
}
}
}
</script>
<style scoped="true">
.selfMsg{
float: right;
}
</style>
3.5 最終效果
用兩個(gè)不同的瀏覽器,分別登錄admin賬號(hào)和wskh賬號(hào)進(jìn)行聊天測試,效果如下(左邊為admin):

到此這篇關(guān)于使用WebSocket+SpringBoot+Vue搭建簡易網(wǎng)頁聊天室的實(shí)現(xiàn)代碼的文章就介紹到這了,更多相關(guān)WebSocket+SpringBoot+Vue 搭建網(wǎng)頁聊天室內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue2 使用@vue/composition-api依賴包 編譯、打包各種報(bào)錯(cuò)問題分析
由于package.json 文件中 vue、vue-template-compiler 版本號(hào)前面 多了個(gè) ^ 導(dǎo)致實(shí)際導(dǎo)入時(shí),node_module中的 vue 版本可能被升級(jí)為 2.7.x,這篇文章主要介紹了vue2 使用@vue/composition-api依賴包 編譯、打包各種報(bào)錯(cuò)問題分析,需要的朋友可以參考下2023-01-01
vue el-table 動(dòng)態(tài)添加行與刪除行的實(shí)現(xiàn)
這篇文章主要介紹了vue el-table 動(dòng)態(tài)添加行與刪除行的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Vue項(xiàng)目之ES6裝飾器在項(xiàng)目實(shí)戰(zhàn)中的應(yīng)用
作為一個(gè)曾經(jīng)的Java?coder,當(dāng)?shù)谝淮慰吹絡(luò)s里面的裝飾器Decorator,就馬上想到了Java中的注解,當(dāng)然在實(shí)際原理和功能上面,Java的注解和js的裝飾器還是有很大差別的,這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目之ES6裝飾器在項(xiàng)目實(shí)戰(zhàn)中應(yīng)用的相關(guān)資料,需要的朋友可以參考下2022-06-06
Vue生命周期activated之返回上一頁不重新請求數(shù)據(jù)操作
這篇文章主要介紹了Vue生命周期activated之返回上一頁不重新請求數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Vue3?路由頁面切換動(dòng)畫?animate.css效果
這篇文章主要介紹了Vue3路由頁面切換動(dòng)畫animate.css效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
vue點(diǎn)擊導(dǎo)航頁面實(shí)現(xiàn)自動(dòng)滾動(dòng)到特定位置
這篇文章主要介紹了vue點(diǎn)擊導(dǎo)航頁面實(shí)現(xiàn)自動(dòng)滾動(dòng)到特定位置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03

