当前位置:   article > 正文

SpringBoot+webSocket项目私信聊天功能实现_私信功能实现

私信功能实现

webSocket

        什么是ws?

         WS(WebSocket)是一种网络通信协议,它提供了在客户端和服务器之间进行双向、实时通信的能力。相比于传统的HTTP协议,WebSocket具有更低的延迟和更高的效率。

传统的HTTP协议是一种无状态的协议,每次通信都需要客户端发起请求,服务器响应后关闭连接,因此无法实现实时的双向通信。而WebSocket协议则在初始握手阶段首先通过HTTP协议建立连接,然后升级为双向通信的WebSocket连接。一旦建立了WebSocket连接,客户端和服务器就可以通过该连接进行实时的数据交换,而无需每次都重新建立连接。

WebSocket协议使用基于帧的消息传递机制,允许客户端和服务器以消息的形式进行通信。客户端可以发送消息给服务器,服务器也可以主动推送消息给客户端,实现了真正的双向通信。这种实时通信的特性使得WebSocket在许多场景下非常有用,例如在线聊天应用、实时数据监控和游戏等。

SpringBoot集成WebSocket

        在Java项目中的pom.xml文件中导入依赖wzbsocket依赖

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

     导入websocket配置文件注册成为一个工具类,交给ioc管理。加载websocket

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  4. /**
  5. * 开启WebSocket支持
  6. * @author wzk
  7. */
  8. @Configuration
  9. public class WebSocketConfig {
  10. @Bean
  11. public ServerEndpointExporter serverEndpointExporter() {
  12. return new ServerEndpointExporter();
  13. }
  14. }

        加载业务逻辑

websocket方法:

      @ServerEndpoint(value = "/chat/{username}"):用于建立连接路径,username表示当前连接的用户(前端的连接 路径为:ws://localhost:端口号/chat/当前连接用户)

     @Component:当前类注入ioc管理对方

     @OnOpen:用户发送请求触发函数方法

     @PathParam("name"):将用户请求路径中的 参数注入到该方法的参数上面

     @OnError : 用户请求连接异常触发函数

     @OnMessage:用户客户端*(前端)项目服务端(后端)发送信息触发函数

     @OnClose : 用户关闭连接触发函数

      发送消息方法sendMessage :toSession.getBasicRemote().sendText(msg):通过toSessiongetBasicRemote()方法获取到与目标会话关联的RemoteEndpoint.Basic对象,通过调用sendText(msg)方法发送文本消息。sendText方法是WebSocket API提供的方法,用于发送文本消息至远程端点。

  1. import com.alibaba.fastjson.JSON;
  2. import com.hqyj.project.pojo.Message;
  3. import com.hqyj.project.utils.ApplicationContextUtil;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.stereotype.Component;
  7. import javax.websocket.*;
  8. import javax.websocket.server.PathParam;
  9. import javax.websocket.server.ServerEndpoint;
  10. import java.io.IOException;
  11. import java.util.List;
  12. import java.util.concurrent.ConcurrentHashMap;
  13. @ServerEndpoint(value = "/chat/{username}")
  14. @Component
  15. @Slf4j
  16. public class SocketServer {
  17. // 保存链接的session,key为用户名,value为对应的session名
  18. private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
  19. /**
  20. * 创建连接
  21. * 用于监听建立连接,当有客户端与该服务端点建立连接时,将会自回调该注解标注的方法
  22. * @param session
  23. * @param username
  24. */
  25. @OnOpen
  26. public void onOpen(Session session, @PathParam(value = "username") String username) {
  27. log.info("用户{}已创建连接", username);
  28. String key = "webSocket:"+username;
  29. sessionMap.put(username,session);
  30. //获取redis对象
  31. RedisTemplate redisTemplate =(RedisTemplate)ApplicationContextUtil.getBean("redis");
  32. //判断是否为空
  33. List<String> members = redisTemplate.opsForList().range(key,0,-1);
  34. if (null != members && !members.isEmpty()){
  35. for (String message : members) {
  36. Session toSession = sessionMap.get(username);
  37. //发送缓存信息
  38. sendMessage(toSession,message);
  39. }
  40. //删除redis中的键
  41. redisTemplate.delete(key);
  42. }
  43. }
  44. /**
  45. * 用于监听客户端向服务端发送消息,当客户端与服务端发送消息时,将会回调该注解标注的方法
  46. * @param msg
  47. * @param username
  48. */
  49. @OnMessage
  50. public void onMessage(String msg,@PathParam(value = "username") String username){
  51. log.info("用户{}发来消息:{}",username,msg);
  52. //解析用户发送的信息,使用系列化,映射到Message类
  53. Message message = JSON.parseObject(msg, Message.class);
  54. //使用工具类获取redis缓存数据库
  55. RedisTemplate redisTemplate =(RedisTemplate)ApplicationContextUtil.getBean("redis");
  56. //根据message中的to属性获取接收消息的用户的session,利用其session将消息转发过
  57. if (null == sessionMap.get(message.getTo())){ //发送给的用户为空
  58. //存入redis
  59. redisTemplate.opsForList().rightPush("webSocket:"+message.getTo(),message.getMsg());
  60. return;
  61. }
  62. Session toSession = sessionMap.get(message.getTo());
  63. //发送信息给用户
  64. sendMessage(toSession, message.getMsg());
  65. }
  66. /**
  67. * 用于监听连接关闭,当客户端与该服务端点断开连接时,将会回调该注解标注的方法
  68. * @param session
  69. * @param username
  70. */
  71. @OnClose
  72. public void onClose(Session session, @PathParam(value = "username") String username){
  73. log.info("用户{}已关闭连接", username);
  74. sessionMap.remove(username);
  75. }
  76. /**
  77. * 用于监听该连接上的任何错误,当客户端与该服务端点的连接发生任何异常,都将回调该注解标注的方法
  78. * 注意该方法的参数必选Throwable,可选Sessiion以及0-n个String参数,且String参数需要使用@PathParam注解标注
  79. * @param throwable
  80. * @param username
  81. */
  82. @OnError
  83. public void onError(Throwable throwable,@PathParam(value = "username") String username){
  84. log.error("用户{}连接发生异常", username);
  85. }
  86. /**
  87. * 用来发送消息的方法,参数分别为接收消息的用户的session,和对应的消息
  88. */
  89. private void sendMessage(Session toSession,String msg){
  90. try {
  91. toSession.getBasicRemote().sendText(msg);
  92. } catch (IOException e) {
  93. throw new RuntimeException(e);
  94. }
  95. }
  96. }

前端Vue代码

        代码解析注释里面已经存在,就不过多阐述

  1. <template>
  2. <div class="chat-container">
  3. <div class="chat-header">
  4. <span class="chat-header-text">{{ userFriend.userFriendNickname }}</span>
  5. </div>
  6. <div class="chat-messages">
  7. <div v-for="message in messages" :key="message.id" class="chat-message">
  8. <div v-if="message.email === user.email " class="chat-message-right">
  9. <div class="chat-message-author">{{ message.nickName }}</div>
  10. <div class="chat-message-avatar" :style="{ backgroundImage: `url(${message.cover})` }"></div>
  11. <div class="chat-message-text">{{ message.text }}</div>
  12. </div>
  13. <div v-else class="chat-message-left">
  14. <div class="chat-message-author">{{ message.nickName }}</div>
  15. <div class="chat-message-avatar" :style="{ backgroundImage: `url(${message.cover})` }"></div>
  16. <div class="chat-message-text">{{ message.text }}</div>
  17. </div>
  18. </div>
  19. <div v-if="messages.length === 0" class="no-message-tip">暂无消息</div>
  20. </div>
  21. <div class="chat-input-container">
  22. <input v-model="inputText" placeholder="输入消息" @keydown.enter="send" />
  23. <button @click="send">发送</button>
  24. </div>
  25. </div>
  26. </template>
  27. <script>
  28. // 创建websocket
  29. let webSocket = null;
  30. export default {
  31. name: "Chat",
  32. data() {
  33. return {
  34. // toUser: "",
  35. messages: [],
  36. // userName: "",
  37. inputText: "",
  38. messageId: 1,
  39. user:{
  40. cover:"",
  41. email:"",
  42. nickName:""
  43. },
  44. userFriend:{
  45. userEmail:"",
  46. userFriendEmail:"",
  47. userFriendNickname:"",
  48. userFriendCover:""
  49. }
  50. };
  51. },
  52. methods: {
  53. sendMessage(message) { //当前账号发送信息
  54. if (message !== "") {
  55. this.messages.push({
  56. id: this.messageId++,
  57. email:this.user.email, //当前账号
  58. nickName: this.user.nickName, //当前账号昵称
  59. text: message, //当前发送的信息
  60. cover: this.user.cover //当前头像的url
  61. });
  62. // console.log(this.messages)
  63. this.inputText = "";
  64. }
  65. },
  66. responseMessage(message) { //响应信息
  67. if (message !== "") {
  68. this.messages.push({
  69. id: this.messageId++,
  70. email:this.userFriend.userFriendEmail,
  71. nickName: this.userFriend.userFriendNickname, //对方昵称
  72. text: message, //对方发送的信息
  73. cover: this.userFriend.userFriendCover //对方头像的url
  74. });
  75. // console.log(this.messages)
  76. this.inputText = "";
  77. }
  78. },
  79. connectWebSocket() { //获取链接
  80. const userName = this.user.email; //当前账号
  81. console.log(this.userFriend)
  82. const target = "ws://localhost:8081/chat/" + userName;
  83. if ("WebSocket" in window) { //判断浏览器是否支持,创建websocket对象
  84. webSocket = new WebSocket(target);
  85. } else {
  86. alert("浏览器不支持websocket");
  87. }
  88. webSocket.onerror = function () {
  89. alert("发生错误连接失败");
  90. };
  91. webSocket.onopen = function () {
  92. console.log("连接成功");
  93. };
  94. webSocket.onmessage = (res) => {
  95. console.log(res)
  96. this.responseMessage(res.data); //res.data对方响应的信息
  97. };
  98. webSocket.onclose = function () {
  99. // this.sendMessage("Loc MSG:关闭连接");
  100. console.log("关闭连接");
  101. };
  102. window.onbeforeunload = function () {
  103. webSocket.close();
  104. };
  105. },
  106. closeWebSocket() {
  107. webSocket.close();
  108. },
  109. send() {
  110. const msg = this.inputText.trim();
  111. if (msg !== "") {
  112. const toUser = this.userFriend.userFriendEmail;
  113. const chatMsg = {
  114. msg: msg,
  115. to: toUser
  116. };
  117. webSocket.send(JSON.stringify(chatMsg));
  118. this.sendMessage(msg);
  119. }
  120. },
  121. sendToUser() {
  122. //获取当前用户账号和发送信息用户账号
  123. this.user = JSON.parse(sessionStorage.getItem("user"));
  124. // console.log(this.user)
  125. const userFriend = JSON.parse(sessionStorage.getItem("userFriendEmail"));
  126. // console.log(userFriend)
  127. this.userFriend.userFriendEmail = userFriend.userFriendEmail;
  128. this.userFriend.userFriendNickname = userFriend.userFriendNickname;
  129. this.userFriend.userFriendCover = userFriend.userFriendCover;
  130. // console.log(this.userFriend)
  131. },
  132. },
  133. mounted() {
  134. this.sendToUser();
  135. this.connectWebSocket();
  136. },
  137. };
  138. </script>
  139. <style scoped>
  140. .chat-container {
  141. width: 400px;
  142. height: 500px;
  143. border-radius: 5px;
  144. box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  145. display: flex;
  146. flex-direction: column;
  147. }
  148. .chat-header {
  149. padding: 10px;
  150. background-color: #1890ff;
  151. color: #fff;
  152. font-size: 18px;
  153. font-weight: bold;
  154. display: flex;
  155. justify-content: center;
  156. align-items: center;
  157. }
  158. .chat-header-text {
  159. width: 100%;
  160. text-align: center;
  161. white-space: nowrap;
  162. overflow: hidden;
  163. text-overflow: ellipsis;
  164. }
  165. .chat-messages {
  166. flex-grow: 1;
  167. padding: 10px;
  168. overflow-y: auto;
  169. background-color: #f5f5f5;
  170. border-top: 1px solid #ccc;
  171. border-bottom: 1px solid #ccc;
  172. }
  173. .chat-message {
  174. margin-bottom: 10px;
  175. }
  176. .chat-message-left {
  177. display: flex;
  178. flex-direction: column;
  179. align-items: flex-start;
  180. }
  181. .chat-message-avatar {
  182. /*变为行元素*/
  183. display: inline;
  184. width: 40px;
  185. height: 40px;
  186. background-size: cover;
  187. background-position: center;
  188. border-radius: 50%;
  189. margin-right: 10px;
  190. }
  191. .chat-message-author {
  192. font-weight: bold;
  193. margin-bottom: 5px;
  194. }
  195. .chat-message-right .chat-message-text {
  196. display: inline;
  197. position:relative;
  198. top: -40px;
  199. right: 55px;
  200. word-wrap: break-word;
  201. max-width: 200px;
  202. padding: 8px;
  203. background-color: #fff;
  204. border-radius: 5px;
  205. box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
  206. }
  207. .chat-message-left .chat-message-text {
  208. display: inline;
  209. position:relative;
  210. top: -40px;
  211. right: -45px;
  212. word-wrap: break-word;
  213. max-width: 200px;
  214. padding: 8px;
  215. background-color: #fff;
  216. border-radius: 5px;
  217. box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
  218. }
  219. .chat-message-right {
  220. display: flex;
  221. flex-direction: column;
  222. align-items: flex-end;
  223. }
  224. .chat-input-container {
  225. display: flex;
  226. align-items: center;
  227. padding: 10px;
  228. background-color: #f5f5f5;
  229. border-top: 1px solid #ccc;
  230. }
  231. .chat-input-container input {
  232. flex-grow: 1;
  233. height: 30px;
  234. margin-right: 10px;
  235. padding: 5px;
  236. border-radius: 5px;
  237. border: 1px solid #ccc;
  238. outline: none;
  239. font-size: 14px; /* 添加字体大小样式 */
  240. }
  241. .chat-input-container button {
  242. padding: 5px 10px;
  243. background-color: #1890ff;
  244. color: #fff;
  245. border: none;
  246. border-radius: 5px;
  247. cursor: pointer;
  248. outline: none;
  249. font-size: 14px; /* 添加字体大小样式 */
  250. }
  251. </style>

成品图

        

完成全双关双向通信

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

闽ICP备14008679号