当前位置:   article > 正文

SpringBoot整合WebSocket_springboot整合websocket客户端

springboot整合websocket客户端

目录

依赖

config

socket

vue参考


参考:在 Spring Boot 中整合、使用 WebSocket - spring 中文网学习如何在 Spring Boot 中整合、使用 WebSocket,以及如何在 @ServerEndpoint 类中注入其他 Bean 依赖 。icon-default.png?t=N7T8https://springdoc.cn/spring-boot-websocket/

依赖

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

config

  1. @Configuration
  2. @EnableWebSocket
  3. public class WebSocketConfig {
  4. @Bean
  5. public ServerEndpointExporter serverEndpointExporter(){
  6. return new ServerEndpointExporter();
  7. }
  8. }

socket

  1. package cn.wqk.serverwebsocket.socket;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.stereotype.Component;
  4. import javax.websocket.*;
  5. import javax.websocket.server.PathParam;
  6. import javax.websocket.server.ServerEndpoint;
  7. import java.io.IOException;
  8. import java.util.Date;
  9. import java.util.Map;
  10. import java.util.Set;
  11. import java.util.concurrent.ConcurrentHashMap;
  12. import java.util.concurrent.ConcurrentMap;
  13. import java.util.concurrent.CopyOnWriteArraySet;
  14. import java.util.concurrent.atomic.AtomicInteger;
  15. /**
  16. * Created with IntelliJ IDEA.
  17. *
  18. * @Auther: 吴青珂
  19. * @Date: 2021/05/31/16:16
  20. * @Description:
  21. */
  22. @Component
  23. @Slf4j
  24. @ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径
  25. public class WebSocket {
  26. /** 当前在线客户端数量(线程安全的) */
  27. private static AtomicInteger onlineClientNumber = new AtomicInteger(0);
  28. /** 当前在线客户端集合(线程安全的):以键值对方式存储,key是连接的编号,value是连接的对象 */
  29. private static Map<String ,Session> onlineClientMap = new ConcurrentHashMap<>();
  30. /**
  31. * 客户端与服务端连接成功
  32. * @param session
  33. * @param username
  34. */
  35. @OnOpen
  36. public void onOpen(Session session,@PathParam("username") String username){
  37. /*
  38. do something for onOpen
  39. 与当前客户端连接成功时
  40. */
  41. onlineClientNumber.incrementAndGet();//在线数+1
  42. onlineClientMap.put(session.getId(),session);//添加当前连接的session
  43. log.info("时间[{}]:与用户[{}]的连接成功,当前连接编号[{}],当前连接总数[{}]",
  44. new Date().toLocaleString(),
  45. username,
  46. session.getId(),
  47. onlineClientNumber);
  48. }
  49. /**
  50. * 客户端与服务端连接关闭
  51. * @param session
  52. * @param username
  53. */
  54. @OnClose
  55. public void onClose(Session session,@PathParam("username") String username){
  56. /*
  57. do something for onClose
  58. 与当前客户端连接关闭时
  59. */
  60. onlineClientNumber.decrementAndGet();//在线数-1
  61. onlineClientMap.remove(session.getId());//移除当前连接的session
  62. log.info("时间[{}]:与用户[{}]的连接关闭,当前连接编号[{}],当前连接总数[{}]",
  63. new Date().toLocaleString(),
  64. username,
  65. session.getId(),
  66. onlineClientNumber);
  67. }
  68. /**
  69. * 客户端与服务端连接异常
  70. * @param error
  71. * @param session
  72. * @param username
  73. */
  74. @OnError
  75. public void onError(Throwable error,Session session,@PathParam("username") String username) {
  76. /*
  77. do something for onError
  78. 与当前客户端连接异常时
  79. */
  80. error.printStackTrace();
  81. }
  82. /**
  83. * 客户端向服务端发送消息
  84. * @param message
  85. * @param username
  86. * @throws IOException
  87. */
  88. @OnMessage
  89. public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException {
  90. /*
  91. do something for onMessage
  92. 收到来自当前客户端的消息时
  93. */
  94. log.info("时间[{}]:来自连接编号为[{}]的消息:[{}]",
  95. new Date().toLocaleString(),
  96. session.getId(),
  97. message);
  98. sendAllMessage(message);
  99. }
  100. //向所有客户端发送消息(广播)
  101. private void sendAllMessage(String message){
  102. Set<String> sessionIdSet = onlineClientMap.keySet(); //获得Map的Key的集合
  103. for (String sessionId : sessionIdSet) { //迭代Key集合
  104. Session session = onlineClientMap.get(sessionId); //根据Key得到value
  105. session.getAsyncRemote().sendText(message); //发送消息给客户端
  106. }
  107. }
  108. //只向当前客户端发送消息
  109. private void sendOneMessage(String message){
  110. }
  111. }
  1. package com.lyh.mp.socket;
  2. import com.lyh.mp.entity.Message;
  3. import com.lyh.mp.mapper.MessageMapper;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.BeansException;
  7. import org.springframework.context.ApplicationContext;
  8. import org.springframework.context.ApplicationContextAware;
  9. import org.springframework.stereotype.Component;
  10. import javax.websocket.*;
  11. import javax.websocket.server.ServerEndpoint;
  12. import java.io.IOException;
  13. import java.time.Instant;
  14. // 使用 @ServerEndpoint 注解表示此类是一个 WebSocket 端点
  15. // 通过 value 注解,指定 websocket 的路径
  16. @ServerEndpoint(value = "/channel/echo")
  17. @Component
  18. public class EchoChannel implements
  19. ApplicationContextAware {
  20. private static final Logger LOGGER = LoggerFactory.getLogger(EchoChannel.class);
  21. private Session session;
  22. // 全局静态变量,保存 ApplicationContext
  23. private static ApplicationContext applicationContext;
  24. // MessageMapper.
  25. private MessageMapper messageMapper;
  26. // 收到消息
  27. @OnMessage
  28. public void onMessage(String message) throws IOException {
  29. /*
  30. 前端需要传入,发送者id,接收者id
  31. */
  32. Message messageA = new Message();
  33. messageA.setReceiverId(1L);
  34. messageA.setSenderId(2L);
  35. messageA.setMessageText(message);
  36. messageMapper.insert(messageA);
  37. LOGGER.info("[websocket] 收到消息:id={},message={}", this.session.getId(), message);
  38. if (message.equalsIgnoreCase("bye")) {
  39. // 由服务器主动关闭连接。状态码为 NORMAL_CLOSURE(正常关闭)。
  40. this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"));
  41. return;
  42. }
  43. this.session.getAsyncRemote().sendText("[" + Instant.now().toEpochMilli() + "] Hello " + message);
  44. }
  45. // 连接打开
  46. @OnOpen
  47. public void onOpen(Session session, EndpointConfig endpointConfig) {
  48. // 保存 session 到对象
  49. this.session = session;
  50. this.messageMapper = EchoChannel.applicationContext.getBean(MessageMapper.class);
  51. LOGGER.info("[websocket] 新的连接:id={}", this.session.getId());
  52. }
  53. // 连接关闭
  54. @OnClose
  55. public void onClose(CloseReason closeReason) {
  56. LOGGER.info("[websocket] 连接断开:id={},reason={}", this.session.getId(), closeReason);
  57. }
  58. // 连接异常
  59. @OnError
  60. public void onError(Throwable throwable) throws IOException {
  61. LOGGER.info("[websocket] 连接异常:id={},throwable={}", this.session.getId(), throwable.getMessage());
  62. // 关闭连接。状态码为 UNEXPECTED_CONDITION(意料之外的异常)
  63. this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
  64. }
  65. @Override
  66. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  67. EchoChannel.applicationContext = applicationContext;
  68. }
  69. }

vue参考

  1. <template>
  2. <div>
  3. <table>
  4. <thead>
  5. <tr>
  6. <th>消息编号</th>
  7. <th>发送者</th>
  8. <th>发送时间</th>
  9. <th>发送内容</th>
  10. </tr>
  11. </thead>
  12. <tbody>
  13. <tr v-for="item in messageList" :key="item.time">
  14. <td>{{ item.id }}</td>
  15. <td>{{ item.username }}</td>
  16. <td>{{ new Date(item.time).toLocaleTimeString() }}</td>
  17. <td>{{ item.message }}</td>
  18. </tr>
  19. </tbody>
  20. </table>
  21. <input
  22. type="text"
  23. v-model="sendMessage"
  24. placeholder="请输入你要发送的消息">
  25. <button @click="handleSendButton()">发送</button>
  26. <button @click="handleLogoutButton()">退出</button>
  27. </div>
  28. </template>
  29. <script>
  30. import {
  31. getUsername,
  32. removeUsername
  33. } from "@/utils/username";
  34. export default {
  35. name: "Home",
  36. data() {
  37. return {
  38. webSocketObject: null,
  39. username: '',
  40. messageList: [
  41. ],
  42. sendMessage: ''
  43. }
  44. },
  45. created() {
  46. //从localStorage中获得username
  47. this.username = getUsername()
  48. //如果username不存在返回到登录页面
  49. if (!this.username){
  50. this.$router.push({
  51. name: 'Login'
  52. })
  53. }
  54. //初始化WebSocket
  55. this.webSocketInit()
  56. },
  57. beforeDestroy() {
  58. this.webSocketObject.close();//在该组件销毁时关闭该连接以节约资源
  59. },
  60. methods: {
  61. webSocketInit(){
  62. const webSocketUrl = 'ws://localhost:8000/websocket/'+this.username
  63. this.webSocketObject = new WebSocket(webSocketUrl);
  64. this.webSocketObject.onopen = this.webSocketOnOpen
  65. this.webSocketObject.onmessage = this.webSocketOnMessage
  66. this.webSocketObject.onerror = this.webSocketOnError
  67. this.webSocketObject.onclose = this.webSocketOnClose
  68. },
  69. webSocketOnOpen(e){
  70. console.log('与服务端连接打开->',e)
  71. },
  72. webSocketOnMessage(e){
  73. console.log('来自服务端的消息->',e)
  74. const receiveMessage = JSON.parse(e.data);
  75. this.messageList.push(receiveMessage)
  76. },
  77. webSocketOnError(e){
  78. console.log('与服务端连接异常->',e)
  79. },
  80. webSocketOnClose(e){
  81. console.log('与服务端连接关闭->',e)
  82. },
  83. handleSendButton() {
  84. const username = this.username
  85. const message = this.sendMessage
  86. this.webSocketObject.send(JSON.stringify({
  87. id: 1,
  88. message,
  89. username,
  90. time: new Date().getTime()
  91. }))
  92. this.sendMessage = ''
  93. },
  94. handleLogoutButton(){
  95. removeUsername() //清除username然后断开连接
  96. this.webSocketObject.close();
  97. this.$router.push({
  98. name: 'Login'
  99. })
  100. }
  101. },
  102. }
  103. </script>
  104. <style scoped>
  105. </style>

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

闽ICP备14008679号