h5效果图

vue效果图

功能实现
spring boot + webSocket 实现
官方地址
https://docs.spring.io/spring-framework/docs/5.0.8.RELEASE/spring-framework-reference/web.html#websocket
maven 配置文件
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.2.0.RELEASE</version></parent><groupId>org.example</groupId><artifactId>webChat</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency><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.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></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><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>
webSocket 配置
package com.example.webchat.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;/*** @author Mr.Fang* @title: WebSocketConfig* @Description: web socket 配置* @date 2021/11/14 13:12*/@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(myHandler(), "myHandler/") // 访问路径.addInterceptors(new WebSocketHandlerInterceptor()) // 配置拦截器.setAllowedOrigins("*"); // 跨域}@Beanpublic ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();container.setMaxTextMessageBufferSize(8192); // 例如消息缓冲区大小、空闲超时等container.setMaxBinaryMessageBufferSize(8192);return container;}@Beanpublic WebSocketHandler myHandler() {return new MyHandler();}}
消息处理类
package com.example.webchat.config;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.example.webchat.pojo.DataVo;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.handler.AbstractWebSocketHandler;import java.io.IOException;import java.util.*;import java.util.concurrent.ConcurrentHashMap;/*** @author Mr.Fang* @title: MyHandler* @Description: 消息处理类* @date 2021/11/14 13:12*/public class MyHandler extends AbstractWebSocketHandler {private static int onlineCount = 0;// 线程安全private static Map<String, WebSocketSession> userMap = new ConcurrentHashMap<>(); // 用户private static Map<String, WebSocketSession> adminMap = new ConcurrentHashMap<>(); // 客服/*** @Description: 连接成功之后* @param session* @return void* @Author Mr.Fang* @date 2021/11/14 13:15*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws IOException {addOnlineCount(); // 当前用户加 1System.out.println(session.getId());Map<String, Object> map = session.getAttributes();Object token = map.get("token");Object admin = map.get("admin");DataVo dataVo = new DataVo();dataVo.setCode(9001).setMsg("连接成功");if (Objects.nonNull(admin)) {adminMap.put(session.getId(), session); // 添加客服} else {// 分配客服userMap.put(session.getId(), session); // 添加当前用户distribution(dataVo);}dataVo.setId(session.getId());System.out.println("用户连接成功:" + admin);System.out.println("用户连接成功:" + token);System.out.println("在线用户:" + getOnlineCount());this.sendMsg(session, JSONObject.toJSONString(dataVo));}/*** @param vo* @return void* @Description: 分配客服* @Author Mr.Fang* @date 2021/11/14 13:13*/private void distribution(DataVo vo) {if (adminMap.size() != 0) {Random random = new Random();int x = random.nextInt(adminMap.size());Set<String> values = adminMap.keySet();int j = 0;for (String str : values) {if (j == x) {vo.setRecId(str);System.out.println("分配ID:" + str);break;}j++;}}}/*** @param session* @param message* @return void* @Description: 收发消息* @Author Mr.Fang* @date 2021/11/14 13:13*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {System.out.print("用户ID:" + session.getId());String payload = message.getPayload();System.out.println("接受到的数据:" + payload);DataVo dataVo = JSON.toJavaObject(JSON.parseObject(payload), DataVo.class); // json 转对象if (Objects.isNull(dataVo.getRecId()) || dataVo.getRecId().equals("")) { // 用户客服为空 分配客服WebSocketSession socketSession = adminMap.get(session.getId());if (Objects.isNull(socketSession)) {this.distribution(dataVo);}}if (dataVo.getCode() == 9002) {if (Objects.nonNull(dataVo.getRecId())) { // user -> adminWebSocketSession socketSession = adminMap.get(dataVo.getRecId());dataVo.setSelfId(session.getId()).setRecId("");this.sendMsg(socketSession, JSONObject.toJSONString(dataVo));} else if (Objects.nonNull(dataVo.getSelfId())) { // admin ->userWebSocketSession socketSession = userMap.get(dataVo.getSelfId());dataVo.setRecId(session.getId()).setSelfId("");this.sendMsg(socketSession, JSONObject.toJSONString(dataVo));}}}/*** @param session* @param msg* @return void* @Description: 发送消息* @Author Mr.Fang* @date 2021/11/14 13:14*/private void sendMsg(WebSocketSession session, String msg) throws IOException {session.sendMessage(new TextMessage(msg));}/*** @Description: 断开连接之后* @param session* @param status* @return void* @Author Mr.Fang* @date 2021/11/14 13:14*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) {subOnlineCount(); // 当前用户加 1adminMap.remove(session.getId());userMap.remove(session.getId());System.out.println("用户断开连接token:" + session.getId());System.out.println("用户断开连接admin:" + session.getId());System.out.println("在线用户:" + getOnlineCount());}public static synchronized int getOnlineCount() {return onlineCount;}/*** @Description: 在线用户 +1* @return void* @Author Mr.Fang* @date 2021/11/14 13:16*/public static synchronized void addOnlineCount() {MyHandler.onlineCount++;}/*** @Description: 在线用户 -1* @return void* @Author Mr.Fang* @date 2021/11/14 13:16*/public static synchronized void subOnlineCount() {MyHandler.onlineCount--;}}
配置拦截器
package com.example.webchat.config;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.http.server.ServletServerHttpRequest;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import javax.servlet.http.HttpServletRequest;import java.util.Map;import java.util.Objects;/*** @author Mr.Fang* @title: WebSocketHandlerInterceptor* @Description: 拦截器* @date 2021/11/14 13:12*/public class WebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor {/*** @param request* @param response* @param wsHandler* @param attributes* @return boolean* @Description: 握手之前* @Author Mr.Fang* @date 2021/11/14 13:18*/@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;HttpServletRequest re = servletRequest.getServletRequest();Object token = re.getParameter("token");Object admin = re.getParameter("admin");if (Objects.isNull(token)) {return false;}re.getSession().setAttribute("admin", admin);re.getSession().setAttribute("token", token);return super.beforeHandshake(request, response, wsHandler, attributes);}/*** @param request* @param response* @param wsHandler* @param ex* @return boolean* @Description: 握手之后* @Author Mr.Fang* @date 2021/11/14 13:18*/@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {super.afterHandshake(request, response, wsHandler, ex);}}
h5服务端
<!DOCTYPE html><html><head><meta charset="utf-8"><title>服务端</title><style type="text/css">#client {margin: 0px auto;width: 500px;}input {width: 80%;height: 40px;border-radius: 5px;border-color: #CCCCCC;outline: #01FA01;}#button {width: 84px;height: 46px;background-color: #5af3a5;color: #fff;font-size: 20px;border-radius: 5px;border: none;box-shadow: 1px 1px 1px 1px #ccc;cursor: pointer;outline: #01FA01;}</style></head><body><div id="client"><h1 style="text-align: center;">服务端发送消息</h1><div id="content" contenteditable=truestyle="width: 500px;height: 500px;margin: 0px auto;border: 1px solid #000000;padding: 10px;border-radius: 10px;overflow: auto;"></div><div style="padding: 5px;0px"><input type="" value="" /> <button id="button" type="button">发送</button></div></div><script src="http://code.jquery.com/jquery-2.1.1.min.js"></script><script type="text/javascript">$(() => {var pushData = {code: 9002,msg: '',selfId: '',};var time = null;var path = 'ws://127.0.0.1:8009/myHandler/';if (typeof(WebSocket) === "undefined") {alert('不支持websocket')return;}let id = Math.random(); // 随机数// 实例化socketvar webSocket = new WebSocket(path + '?token=' + id+'&admin=1');// 监听连接webSocket.onopen = function(event) {console.log(event);interval();};// 监听消息webSocket.onmessage = function(event) {let data = JSON.parse(event.data);pushData.selfId = data.selfId;if (data.code == 9002) {$('#content').append(`<p style="text-align: right;"><span style="color:chocolate;">${data.msg}</span>:客户端</p>`)} else if (data.code == 9001) {$('#content').append(`<p style="color:#a09b9b;text-align:center;" >连接成功</p>`);}console.log(event)};// 监听错误webSocket.onerror = function(event) {console.log(event)$('#content').append(`<p style="color:#a09b9b;text-align:center;" >连接错误</p>`);clearInterval();};// 发送消息$('#button').click(() => {let v = $('input').val();if (v) {pushData.code = 9002;pushData.msg = v;webSocket.send(JSON.stringify(pushData));$('#content').append(`<p>服务端:<span style="color: blueviolet;">${v}</span></p>`)$('input').val('');}})function interval() {time = setInterval(() => {pushData.code = 9003;pushData.msg = '心跳';webSocket.send(JSON.stringify(pushData));}, 5000);}function clearInterval() {clearInterval(time);}})</script></body></html>
客户端
<!DOCTYPE html><html><head><meta charset="utf-8"><title>客户端</title><style type="text/css">#client {margin: 0px auto;width: 500px;}input {width: 80%;height: 40px;border-radius: 5px;border-color: #CCCCCC;outline: #01FA01;}#button {width: 84px;height: 46px;background-color: #5af3a5;color: #fff;font-size: 20px;border-radius: 5px;border: none;box-shadow: 1px 1px 1px 1px #ccc;cursor: pointer;outline: #01FA01;}</style></head><body><div id="client"><h1 style="text-align: center;">客户端发送消息</h1><div id="content" contenteditable=truestyle="width: 500px;height: 500px;margin: 0px auto;border: 1px solid #000000;padding: 10px;border-radius: 10px;overflow: auto;"></div><div style="padding: 5px;0px"><input type="" value="" /> <button id="button" type="button">发送</button></div></div><script src="http://code.jquery.com/jquery-2.1.1.min.js"></script><script type="text/javascript">$(() => {var pushData = {code: 9002,msg: '',recId: '',};var time = null;var path = 'ws://127.0.0.1:8009/myHandler/';if (typeof(WebSocket) === "undefined") {alert('不支持websocket')return;}let id = Math.random(); // 随机数// 实例化socketvar webSocket = new WebSocket(path + '?token=' + id);// 监听连接webSocket.onopen = function(event) {console.log(event);interval();};// 监听消息webSocket.onmessage = function(event) {let data = JSON.parse(event.data);if (data.code == 9002) {$('#content').append(`<p style="text-align: right;"><span style="color:chocolate;">${data.msg}</span>:服务端</p>`)} else if (data.code == 9001) {$('#content').append(`<p style="color:#a09b9b;text-align:center;" >连接成功</p>`);}console.log(event)};// 监听错误webSocket.onerror = function(event) {console.log(event)$('#content').append(`<p style="color:#a09b9b;text-align:center;" >连接错误</p>`);clearInterval();};// 发送消息$('#button').click(() => {let v = $('input').val();if (v) {pushData.code = 9002;pushData.msg = v;webSocket.send(JSON.stringify(pushData));$('#content').append(`<p>客户端:<span style="color: blueviolet;">${v}</span></p>`)$('input').val('');}})function interval() {time = setInterval(() => {pushData.code = 9003;pushData.msg = '心跳';webSocket.send(JSON.stringify(pushData));}, 5000);}function clearInterval() {clearInterval(time);}})</script></body></html>
vue 连接 webSocket
<template><div class="chat"><van-nav-bar fixed placeholder title="聊天内容" left-arrow /><div id="content" ref="rightBody"><div v-for="item in list" :key="item.id"><div class="chat-model" v-if="item.isSelf"><div><van-image width="45px" height="45px" fit="fill" round src="https://img01.yzcdn.cn/vant/cat.jpeg" /></div><div class="chat-content chat-content-l">{{item.content}}</div></div><div class="chat-model" style="justify-content: flex-end" v-else><div class="chat-content chat-content-r">{{item.content}}</div><div><van-image width="45px" height="45px" fit="fill" round src="https://img01.yzcdn.cn/vant/cat.jpeg" /></div></div></div></div><div id="bottom"><input type="text" v-model="text" /><van-button @click="onSend">发送</van-button></div></div></template><script>export default {name: 'HelloWorld',data() {return {path: "ws://192.168.31.156:8009/myHandler/", // socket 地址socket: "",text: '',data: {code: 9002,msg: '',recId: '',},list: [],time: '', // 定时器}},created() {this.init()},methods: {onSend() {if (this.socket.readyState != 1) {this.$toast('连接失败请重新进入');return;}if (!this.text) {this.$toast('请输入内容')return;}var data = {avator: 'https://img01.yzcdn.cn/vant/cat.jpeg',content: this.text,isSelf: false}this.list.push(data);this.send()this.text = '';this.$refs.rightBody.scrollTop = this.$refs.rightBody.scrollHeight;},init: function() {// 0 CONNECTING 连接尚未建立// 1 OPEN WebSocket的链接已经建立// 2 CLOSING 连接正在关闭// 3 CLOSED 连接已经关闭或不可用if (typeof(WebSocket) === "undefined") {this.$toast('您的浏览器不支持socket')} else {let id = Math.random(); // 随机数// 实例化socketthis.socket = new WebSocket(this.path + '?token=' + id);// 监听socket连接this.socket.onopen = this.open// 监听socket错误信息this.socket.onerror = this.error// 监听socket消息this.socket.onmessage = this.getMessage// this.onHeartbeat(); // 心跳防止断开连接}},open: function() {this.$toast('连接成功')},error: function() {this.$toast('连接失败')},getMessage: function(res) {let t = JSON.parse(res.data);var data = {avator: 'https://img01.yzcdn.cn/vant/cat.jpeg',content: t.msg,isSelf: true}if (t.code == 9002) {this.list.push(data);}this.data.recId = t.recId;this.$refs.rightBody.scrollTop = this.$refs.rightBody.scrollHeight;},send: function() {if (this.socket) {this.data.code = 9002;this.data.msg = this.text;this.socket.send(JSON.stringify(this.data))}},close: function() {console.log("socket已经关闭")},onHeartbeat() {var time = setInterval(() => {this.data.code = 9003;this.data.msg = '心跳';this.socket.send(JSON.stringify(this.data))}, 5000);this.time = time;}},destroyed() {// 销毁监听clearInterval(this.time);this.socket.onclose = this.close}}</script><style>.chat {height: 100vh;background-color: #f1f1f3;}#content {overflow: auto;height: 100vh;padding-bottom: 100px;background-color: #f1f1f3;}#bottom {position: fixed;bottom: 0px;width: 100%;display: flex;justify-content: space-evenly;padding: 10px 0px;background-color: #F1F1F3;}#bottom input {background-color: white;width: 72%;height: 30px;padding: 3px 5px;vertical-align: sub;border-style: none;border-radius: 5px;}#bottom button {height: 32px;background-color: rgb(245, 158, 1);border-radius: 5px;color: #fff;}.chat-model {display: flex;flex-direction: row;margin: 10px 10px;margin-top: 30px;align-items: center;}.chat-content {position: relative;max-width: 67%;word-break: break-all;word-wrap: break-word;top: 18px;padding: 10px;border-radius: 5px;background-color: white;}.chat-content-r {right: 10px;}.chat-content-l {left: 10px;}</style>
源码地址
https://gitee.com/bxmms/web-chat.git
天葬
cnblogs.com/bxmm/p/15552285.html

文章转载自IT哈哈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




