当前位置:   article > 正文

配置Spring Boot WebSocket 客户端与前端vue实现多人通信聊天_springboot + vue websocket多人

springboot + vue websocket多人

后台引入

  1. <!-- websocket -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-websocket</artifactId>
  5. </dependency>

然后配置类配置下

  1. @Configuration
  2. public class WebSocketConfig {
  3. /**
  4. * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
  5. */
  6. @Bean
  7. public ServerEndpointExporter serverEndpointExporter() {
  8. return new ServerEndpointExporter();
  9. }
  10. }

然后是核心代码:

  1. package com.example.springboot.component;
  2. import org.springframework.stereotype.Component;
  3. import javax.websocket.server.ServerEndpoint;
  4. import cn.hutool.json.JSONArray;
  5. import cn.hutool.json.JSONObject;
  6. import cn.hutool.json.JSONUtil;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import org.springframework.stereotype.Component;
  10. import javax.websocket.*;
  11. import javax.websocket.server.PathParam;
  12. import javax.websocket.server.ServerEndpoint;
  13. import java.util.Map;
  14. import java.util.concurrent.ConcurrentHashMap;
  15. @ServerEndpoint(value = "/imserver/{username}")
  16. @Component
  17. public class WebSocketServer {
  18. private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
  19. /**
  20. * 记录当前在线连接数
  21. */
  22. public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
  23. /**
  24. * 连接建立成功调用的方法
  25. */
  26. @OnOpen
  27. public void onOpen(Session session, @PathParam("username") String username) {
  28. sessionMap.put(username, session);
  29. log.info("有新用户加入,username={}, 当前在线人数为:{}", username, sessionMap.size());
  30. JSONObject result = new JSONObject();
  31. JSONArray array = new JSONArray();
  32. result.set("users", array);
  33. for (Object key : sessionMap.keySet()) {
  34. JSONObject jsonObject = new JSONObject();
  35. jsonObject.set("username", key);
  36. array.add(jsonObject);
  37. }
  38. sendAllMessage(JSONUtil.toJsonStr(result));
  39. }
  40. /**
  41. * 连接关闭调用的方法
  42. */
  43. @OnClose
  44. public void onClose(Session session, @PathParam("username") String username) {
  45. sessionMap.remove(username);
  46. log.info("有一连接关闭,移除username={}的用户session, 当前在线人数为:{}", username, sessionMap.size());
  47. }
  48. /**
  49. * 收到客户端消息后调用的方法
  50. * 后台收到客户端发送过来的消息
  51. * onMessage 是一个消息的中转站
  52. * 接受 浏览器端 socket.send 发送过来的 json数据
  53. * @param message 客户端发送过来的消息
  54. */
  55. @OnMessage
  56. public void onMessage(String message, Session session, @PathParam("username") String username) {
  57. log.info("服务端收到用户username={}的消息:{}", username, message);
  58. JSONObject obj = JSONUtil.parseObj(message);
  59. String toUsername = obj.getStr("to");
  60. String text = obj.getStr("text");
  61. Session toSession = sessionMap.get(toUsername);
  62. if (toSession != null) {
  63. JSONObject jsonObject = new JSONObject();
  64. jsonObject.set("from", username);
  65. jsonObject.set("text", text);
  66. this.sendMessage(jsonObject.toString(), toSession);
  67. log.info("发送给用户username={},消息:{}", toUsername, jsonObject.toString());
  68. } else {
  69. log.info("发送失败,未找到用户username={}的session", toUsername);
  70. }
  71. }
  72. @OnError
  73. public void onError(Session session, Throwable error) {
  74. log.error("发生错误");
  75. error.printStackTrace();
  76. }
  77. /**
  78. * 服务端发送消息给客户端
  79. */
  80. private void sendMessage(String message, Session toSession) {
  81. try {
  82. log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
  83. toSession.getBasicRemote().sendText(message);
  84. } catch (Exception e) {
  85. log.error("服务端发送消息给客户端失败", e);
  86. }
  87. }
  88. /**
  89. * 服务端发送消息给所有客户端
  90. */
  91. private void sendAllMessage(String message) {
  92. try {
  93. for (Session session : sessionMap.values()) {
  94. log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
  95. session.getBasicRemote().sendText(message);
  96. }
  97. } catch (Exception e) {
  98. log.error("服务端发送消息给客户端失败", e);
  99. }
  100. }
  101. }

接下来就是前端vue

  1. <template>
  2. <div style="padding: 10px; margin-bottom: 50px">
  3. <el-row>
  4. <el-col :span="4">
  5. <el-card style="width: 300px; height: 300px; color: #333">
  6. <div style="padding-bottom: 10px; border-bottom: 1px solid #ccc">在线用户<span style="font-size: 12px">(点击聊天气泡开始聊天)</span></div>
  7. <div style="padding: 10px 0" v-for="user in users" :key="user.username">
  8. <span>{{ user.username }}</span>
  9. <i class="el-icon-chat-dot-round" style="margin-left: 10px; font-size: 16px; cursor: pointer"
  10. @click="chatUser = user.username"></i>
  11. <span style="font-size: 12px;color: limegreen; margin-left: 5px" v-if="user.username === chatUser">chatting...</span>
  12. </div>
  13. </el-card>
  14. </el-col>
  15. <el-col :span="20">
  16. <div style="width: 800px; margin: 0 auto; background-color: white;
  17. border-radius: 5px; box-shadow: 0 0 10px #ccc">
  18. <div style="text-align: center; line-height: 50px;">
  19. 专家咨询--{{ chatUser }}
  20. </div>
  21. <div style="height: 350px; overflow:auto; border-top: 1px solid #ccc" v-html="content"></div>
  22. <div style="height: 200px">
  23. <textarea v-model="text" style="height: 160px; width: 100%; padding: 20px; border: none; border-top: 1px solid #ccc;
  24. border-bottom: 1px solid #ccc; outline: none"></textarea>
  25. <div style="text-align: right; padding-right: 10px">
  26. <el-button type="primary" size="mini" @click="send">发送</el-button>
  27. </div>
  28. </div>
  29. </div>
  30. </el-col>
  31. </el-row>
  32. </div>
  33. </template>
  34. <script>
  35. import request from "@/utils/request";
  36. let socket;
  37. export default {
  38. name: "Im",
  39. data() {
  40. return {
  41. circleUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
  42. user: {},
  43. isCollapse: false,
  44. users: [],
  45. chatUser: '',
  46. text: "",
  47. messages: [],
  48. content: ''
  49. }
  50. },
  51. created() {
  52. this.init()
  53. },
  54. methods: {
  55. send() {
  56. if (!this.chatUser) {
  57. this.$message({type: 'warning', message: "请选择聊天对象"})
  58. return;
  59. }
  60. if (!this.text) {
  61. this.$message({type: 'warning', message: "请输入内容"})
  62. } else {
  63. if (typeof (WebSocket) == "undefined") {
  64. console.log("您的浏览器不支持WebSocket");
  65. } else {
  66. console.log("您的浏览器支持WebSocket");
  67. // 组装待发送的消息 json
  68. // {"from": "zhang", "to": "admin", "text": "聊天文本"}
  69. let message = {from: this.user.username, to: this.chatUser, text: this.text}
  70. socket.send(JSON.stringify(message)); // 将组装好的json发送给服务端,由服务端进行转发
  71. this.messages.push({user: this.user.username, text: this.text})
  72. // 构建消息内容,本人消息
  73. this.createContent(null, this.user.username, this.text)
  74. this.text = '';
  75. }
  76. }
  77. },
  78. createContent(remoteUser, nowUser, text) { // 这个方法是用来将 json的聊天消息数据转换成 html的。
  79. let html
  80. // 当前用户消息
  81. if (nowUser) { // nowUser 表示是否显示当前用户发送的聊天消息,绿色气泡
  82. html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
  83. " <div class=\"el-col el-col-22\" style=\"text-align: right; padding-right: 10px\">\n" +
  84. " <div class=\"tip left\">" + text + "</div>\n" +
  85. " </div>\n" +
  86. " <div class=\"el-col el-col-2\">\n" +
  87. " <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
  88. " <img src=\"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png\" style=\"object-fit: cover;\">\n" +
  89. " </span>\n" +
  90. " </div>\n" +
  91. "</div>";
  92. } else if (remoteUser) { // remoteUser表示远程用户聊天消息,蓝色的气泡
  93. html = "<div class=\"el-row\" style=\"padding: 5px 0\">\n" +
  94. " <div class=\"el-col el-col-2\" style=\"text-align: right\">\n" +
  95. " <span class=\"el-avatar el-avatar--circle\" style=\"height: 40px; width: 40px; line-height: 40px;\">\n" +
  96. " <img src=\"https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png\" style=\"object-fit: cover;\">\n" +
  97. " </span>\n" +
  98. " </div>\n" +
  99. " <div class=\"el-col el-col-22\" style=\"text-align: left; padding-left: 10px\">\n" +
  100. " <div class=\"tip right\">" + text + "</div>\n" +
  101. " </div>\n" +
  102. "</div>";
  103. }
  104. console.log(html)
  105. this.content += html;
  106. },
  107. init() {
  108. this.user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
  109. let username = this.user.username;
  110. let _this = this;
  111. if (typeof (WebSocket) == "undefined") {
  112. console.log("您的浏览器不支持WebSocket");
  113. } else {
  114. // console.log("您的浏览器支持WebSocket");
  115. let socketUrl = "ws://localhost:9090/imserver/" + username;
  116. if (socket != null) {
  117. socket.close();
  118. socket = null;
  119. }
  120. // 开启一个websocket服务
  121. socket = new WebSocket(socketUrl);
  122. //打开事件
  123. socket.onopen = function () {
  124. // console.log("websocket已打开");
  125. };
  126. // 浏览器端收消息,获得从服务端发送过来的文本消息
  127. socket.onmessage = function (msg) {
  128. console.log("收到数据====" + msg.data)
  129. let data = JSON.parse(msg.data) // 对收到的json数据进行解析, 类似这样的: {"users": [{"username": "zhang"},{ "username": "admin"}]}
  130. // console.log(data)
  131. if (data.users) { // 获取在线人员信息
  132. _this.users = data.users.filter(user => user.username !== username) // 获取当前连接的所有用户信息,并且排除自身,自己不会出现在自己的聊天列表里
  133. // console.log(_this.users)
  134. } else {
  135. if (data.from === _this.chatUser) {
  136. _this.messages.push(data)
  137. _this.createContent(data.from, null, data.text)
  138. }
  139. }
  140. };
  141. //关闭事件
  142. socket.onclose = function () {
  143. console.log("websocket已关闭");
  144. };
  145. //发生了错误事件
  146. socket.onerror = function () {
  147. console.log("websocket发生了错误");
  148. }
  149. }
  150. }
  151. }
  152. }
  153. </script>
  154. <style>
  155. .tip {
  156. color: white;
  157. text-align: center;
  158. border-radius: 10px;
  159. font-family: sans-serif;
  160. padding: 10px;
  161. width:auto;
  162. display:inline-block !important;
  163. display:inline;
  164. }
  165. .right {
  166. background-color: deepskyblue;
  167. }
  168. .left {
  169. background-color: forestgreen;
  170. }
  171. </style>

页面做的有些丑,想用自己修改一下吧,反正功能是ok的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/892351
推荐阅读
相关标签
  

闽ICP备14008679号