首先搭建一个SpringBoot项目,其中各个文件:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>love.xiaohh</groupId> <artifactId>websocket-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>websocket-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
love.xiaohh.websocket.WebsocketApplication,其中注册一个bean,使WebSocket的Endpoint生效
package love.xiaohh.websocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @EnableWebSocket // 开启websocket的注解,告诉Spring我们要使用WebSocket @SpringBootApplication public class WebsocketApplication { public static void main(String[] args) { SpringApplication.run(WebsocketApplication.class, args); } /** * 注册一个 ServerEndpointExporter,这样可以让注册到容器当中的WebSocket的Endpoint生效 */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
love.xiaohh.websocket.controllers.ws.WebSocketController,这是主角,也是websocket的控制器,有点像Controller
package love.xiaohh.websocket.controllers.ws; 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.ServerEndpoint; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; /** * <p> * WebSocket的控制器 * </p> * @author tanghai * @version 1.0 * @date 2021-09-14 星期二 11:02:09 */ @Component @ServerEndpoint("/default/websocket") public class WebSocketController { /** * 当前在线的总人数 */ private static final AtomicLong ONLINE_COUNT = new AtomicLong(0L); /** * 所有在线的人数的 */ private static final Map<String, Session> ALL_ONLINE_INFO = new HashMap<String, Session>(); /** * websocket被打开的方法 * * @param session 会话 */ @OnOpen public void open(Session session) { // 获取sessionId,让后将session存入到Map中 final String id = session.getId(); ALL_ONLINE_INFO.put(id, session); // 在线人数自加1并发送给所有的客户端 final long onlineCount = ONLINE_COUNT.incrementAndGet(); this.sendMessage(0, onlineCount + ""); } /** * websocket被退出 * * @param session 会话 */ @OnClose public void close(Session session) { // 将session从缓存当中移除 final String id = session.getId(); ALL_ONLINE_INFO.remove(id); // 在线人数自减1并发送给所有的客户端 final long onlineCount = ONLINE_COUNT.decrementAndGet(); this.sendMessage(0, onlineCount + ""); } /** * 当收到客户端发送过来的消息时触发 * * @param message 客户端发送过来的消息 * @param session 客户端的session会话 */ @OnMessage public void message(String message, Session session) throws IOException { // 将消息发送到所有的客户端 this.sendMessage(1, message); } /** * 发送消息给所有用户 * * @param type 消息类型;0=更新在线人数,1=发送的消息 * @param message 消息实体 */ private void sendMessage(int type, String message) { // 拼接成JSON,让后前端可以解析 String targetMessage = "{\"type\":" + type + ",\"message\":\"" + message + "\"}"; // 遍历并发给所有的客户端 ALL_ONLINE_INFO.values().forEach(session -> { try { session.getBasicRemote().sendText(targetMessage); } catch (IOException e) { e.printStackTrace(); } }); } }
后端的代码大功告成,然后我们搭建一个前端的Vue项目,搭建过程可参见Vue项目的搭建教程,搭建好后需要安装一个element-ui:
npm i element-ui -S
修改几个文件:
index.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>websocket-demo-ui</title> <style type="text/css"> body,#app { margin: 0; border: 0; padding: 0; height: 100%; } </style> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
src/main.js:
import Vue from 'vue' import App from './App' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import router from './router' // 引入并使用elementui Vue.use(ElementUI) Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
src/App.vue:
<template> <router-view/> </template> <script> export default { name: 'App' } </script> <style> </style>
src/router/index.js:
import Vue from 'vue' import Router from 'vue-router' import ChatRoom from '@/components/ChatRoom' Vue.use(Router) export default new Router({ routes: [ { // 根路径默认到聊天室 path: '/', name: 'ChatRoom', component: ChatRoom } ] })
src/components/ChatRoom.vue:
<template> <div id="wrapper"> <el-row style="height: 10%"> <el-col :span="12"><h1>小海海的聊天室</h1></el-col> <el-col :span="12" style="text-align: right;margin: auto 20px auto 0;">当前在线人数: {{ onlineCount }}</el-col> </el-row> <el-row style="height: 80%"> <el-col :span="24"> <chat-item v-for="(message, index) in chatList" :customerMessage="message" :key="index"/> </el-col> </el-row> <el-row> <el-col :span="18"> <el-input v-model="customerMessage" @keydown.enter="sendMessage" placeholder="消息内容..."></el-input> </el-col> <el-col :span="6"> <el-button type="primary" @click="sendMessage" plain>发送</el-button> </el-col> </el-row> </div> </template> <script> import ChatItem from './ChatItem' export default { name: 'ChatRoom', data() { return { socketInfo: { path: 'ws://127.0.0.1:8080/default/websocket', socket: null }, onlineCount: 0, chatList: [], customerMessage: '' } }, mounted() { // 初始化 WebSocket this.init() }, destroyed() { this.socketInfo.socket.close(); }, methods: { /** * 初始化WebSocket */ init() { if (typeof (WebSocket) === 'undefined') { alert('您的浏览器不支持WebSocket') } else { const socket = new WebSocket(this.socketInfo.path) // 发送消息的事件 socket.onmessage = this.getMessage // socket被打卡的事件 socket.onopen = this.socketOpen this.socketInfo.socket = socket } }, socketOpen() { this.chatList.push('您已经进入聊天室,可以开始聊天了!') }, /** * 发送消息给服务端 */ sendMessage() { if (this.customerMessage === '') alert('发送消息不能为空') this.socketInfo.socket.send(this.customerMessage) this.customerMessage = '' }, /** * 收到服务器发过来的消息 * @param message 消息对象 */ getMessage(message) { // 将接收到的消息转换为json格式 const messageJson = JSON.parse(message.data) if (messageJson.type === 0) { this.onlineCount = messageJson.message } else if (messageJson.type === 1) { this.chatList.push(messageJson.message) } } }, components: { ChatItem } } </script> <style scoped> </style>
src/components/ChatItem.vue:
<template> <div> <p>{{customerMessage}}</p> <hr/> </div> </template> <script> export default { name: 'ChatItem', props: { customerMessage: String } } </script> <style scoped> </style>
然后我们启动这两个程序:
# 打包maven项目 mvn clean package -Dmaven.test.skip # 在打包好的好的target目录中启动Java程序 java -jar websocket-demo-0.0.1-SNAPSHOT.jar # 在vue项目的根路径下下载所有依赖: npm install
然后访问http://127.0.0.1:
多开几个:
尝试发送消息发现同步接收:
现在你就可以叫上你们的小伙伴们一起聊天了!如果不行那么欢迎来到代码仓库克隆代码运行哦