参考文章

在SpringBoot中的代码实现

pom文件完整示例

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-freemarker</artifactId>
  8. </dependency>

新建握手拦截器

  1. package socket.config;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.http.server.ServerHttpRequest;
  4. import org.springframework.http.server.ServerHttpResponse;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.socket.WebSocketHandler;
  7. import org.springframework.web.socket.server.HandshakeInterceptor;
  8. import java.util.Map;
  9. /**
  10. * 实现【握手拦截器】
  11. *
  12. * @Slf4j 注解使用解析 https://www.cnblogs.com/sxdcgaq8080/p/11288213.html
  13. * @Component 注解是将本拦截器注入为Bean给Spring容器管理
  14. *
  15. * @author sxd
  16. * @date 2019/8/2 13:47
  17. */
  18. @Slf4j
  19. @Component
  20. public class MyHandshakeInterceptor implements HandshakeInterceptor{
  21. /**
  22. * 重写方法 在握手之前做的事情
  23. * @param serverHttpRequest
  24. * @param serverHttpResponse
  25. * @param webSocketHandler
  26. * @param map
  27. * @return
  28. * @throws Exception
  29. */
  30. @Override
  31. public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) {
  32. return true;
  33. }
  34. /**
  35. * 重写方法 在握手之后做的事情
  36. * @param serverHttpRequest
  37. * @param serverHttpResponse
  38. * @param webSocketHandler
  39. * @param e
  40. */
  41. @Override
  42. public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
  43. }
  44. }

新建webSocket的handler处理器

  1. package socket.config;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.socket.*;
  5. import java.io.IOException;
  6. import java.util.ArrayList;
  7. import java.util.Collections;
  8. import java.util.List;
  9. /**
  10. * 实现WebSocketHandler接口
  11. *
  12. *
  13. * @author sxd
  14. * @date 2019/8/2 14:06
  15. */
  16. @Slf4j
  17. @Component
  18. public class MySocketHander implements WebSocketHandler {
  19. /**
  20. * 为了保存在线用户信息,在方法中新建一个list存储一下【实际项目依据复杂度,可以存储到数据库或者缓存】
  21. */
  22. private final static List<WebSocketSession> SESSIONS = Collections.synchronizedList(new ArrayList<>());
  23. @Override
  24. public void afterConnectionEstablished(WebSocketSession webSocketSession){
  25. log.info("链接成功......");
  26. SESSIONS.add(webSocketSession);
  27. }
  28. @Override
  29. public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) {
  30. log.info("处理要发送的消息");
  31. }
  32. @Override
  33. public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
  34. if (webSocketSession.isOpen()) {
  35. webSocketSession.close();
  36. }
  37. log.info("链接出错,关闭链接......");
  38. SESSIONS.remove(webSocketSession);
  39. }
  40. @Override
  41. public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
  42. log.info("链接关闭......" + closeStatus.toString());
  43. SESSIONS.remove(webSocketSession);
  44. }
  45. @Override
  46. public boolean supportsPartialMessages() {
  47. return false;
  48. }
  49. /**
  50. * 给所有在线用户发送消息
  51. *
  52. * @param message
  53. */
  54. public static void sendMessageToUsers(TextMessage message) {
  55. log.info("给所有在线用户发送消息");
  56. for (WebSocketSession user : SESSIONS) {
  57. try {
  58. if (user.isOpen()) {
  59. user.sendMessage(message);
  60. }
  61. } catch (IOException e) {
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. }

新建webSocket的config配置

  1. package socket.config;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.socket.config.annotation.EnableWebSocket;
  6. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
  7. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
  8. /**
  9. * @author sxd
  10. * @date 2019/8/2 14:43
  11. */
  12. @Slf4j
  13. @Configuration
  14. @EnableWebSocket
  15. public class MyWebSocketConfig implements WebSocketConfigurer {
  16. @Autowired
  17. MyHandshakeInterceptor handshakeInterceptor;
  18. @Autowired
  19. MySocketHander socketHander;
  20. /**
  21. * 实现 WebSocketConfigurer 接口
  22. * 重写 registerWebSocketHandlers 方法,这是一个核心实现方法,配置 websocket 入口,允许访问的域、注册 Handler、SockJs 支持和拦截器。
  23. *
  24. * registry.addHandler()注册和路由的功能
  25. * 当客户端发起 websocket 连接,把 /path 交给对应的 handler 处理,而不实现具体的业务逻辑,可以理解为收集和任务分发中心。
  26. *
  27. *
  28. * addInterceptors() 顾名思义就是为 handler 添加拦截器
  29. * 可以在调用 handler 前后加入我们自己的逻辑代码。
  30. *
  31. *
  32. * setAllowedOrigins(String[] domains),允许指定的域名或 IP (含端口号)建立长连接
  33. * 如果只允许自家域名访问,这里轻松设置。如果不限时使用 * 号,如果指定了域名,则必须要以 http 或 https 开头。
  34. *
  35. * @param registry
  36. */
  37. @Override
  38. public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  39. //部分 支持websocket 的访问链接,允许跨域
  40. registry.addHandler(socketHander, "/ws").addInterceptors(handshakeInterceptor).setAllowedOrigins("*");
  41. //部分 不支持websocket的访问链接,允许跨域
  42. registry.addHandler(socketHander, "/sockjs/ws").addInterceptors(handshakeInterceptor).setAllowedOrigins("*").withSockJS();
  43. }
  44. }

创建chat1.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>测试websocket</title>
  5. <meta charset="UTF-8">
  6. <meta name="viewport"
  7. content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
  8. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
  9. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.2/css/bootstrap.min.css">
  10. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.css">
  11. </head>
  12. <body>
  13. <div class="container">
  14. <div class="input-group mb-3">
  15. <div class="input-group-prepend">
  16. <label class="input-group-text" for="inputGroupSelect01">用户</label>
  17. </div>
  18. <select class="custom-select" id="inputGroupSelect01">
  19. <option selected>选择一个...</option>
  20. </select>
  21. </div>
  22. <div class="input-group mb-3">
  23. <input type="text" class="form-control">
  24. <div class="input-group-append">
  25. <span class="input-group-text" id="btn1">发送给所有人</span>
  26. </div>
  27. </div>
  28. <div class="input-group mb-3">
  29. <input type="text" class="form-control">
  30. <div class="input-group-append">
  31. <span class="input-group-text" id="btn2">发送给单人</span>
  32. </div>
  33. </div>
  34. </div>
  35. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>
  36. <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.2/js/bootstrap.min.js"></script>
  37. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.js"></script>
  38. <script language=javascript>
  39. $(function () {
  40. var websocket;
  41. if ('WebSocket' in window) {
  42. console.log("WebSocket-->");
  43. //new WebSocket()在IDEA打开的任何项目中都可以 直接调用
  44. websocket = new WebSocket("ws://localhost:8020/ws");
  45. } else if ('MozWebSocket' in window) {
  46. console.log("MozWebSocket-->");
  47. websocket = new MozWebSocket("ws://ws");
  48. } else {
  49. console.log("SockJS-->");
  50. websocket = new SockJS("http://127.0.0.1:8020/sockjs/ws");
  51. }
  52. websocket.onopen = function (evnt) {
  53. console.log("链接服务器成功!", evnt.data);
  54. };
  55. websocket.onmessage = function (evnt) {
  56. console.log('收到消息:', evnt.data);
  57. var json = JSON.parse(evnt.data);
  58. if (json.hasOwnProperty('users')) {
  59. var users = json.users;
  60. for (var i = 0; i < users.length; i++) {
  61. $("#inputGroupSelect01").append('<option value="' + users[i] + '">' + users[i] + '</option>');
  62. }
  63. } else {
  64. //打印消息
  65. toast(json.msg, 'info')
  66. }
  67. };
  68. websocket.onerror = function (evnt) {
  69. };
  70. websocket.onclose = function (evnt) {
  71. console.log("与服务器断开了链接!")
  72. }
  73. $('#btn2').bind('click', function () {
  74. if (websocket != null) {
  75. //根据勾选的人数确定是群聊还是单聊
  76. var value = $(this).parent().parent().find('input').val();
  77. //得到选择的用户
  78. var name = $("#inputGroupSelect01").find("option:selected").val();
  79. console.log('选中的用户', name);
  80. if (name === '选择一个...') {
  81. toast('请选择一个用户', 'warning')
  82. } else {
  83. var object = {
  84. to: name,
  85. msg: value,
  86. type: 2
  87. };
  88. //将object转成json字符串发送给服务端
  89. var json = JSON.stringify(object);
  90. websocket.send(json);
  91. }
  92. } else {
  93. console.log('未与服务器链接.');
  94. }
  95. });
  96. $('#btn1').bind('click', function () {
  97. if (websocket != null) {
  98. //根据勾选的人数确定是群聊还是单聊
  99. var value = $(this).parent().parent().find('input').val();
  100. var object = {
  101. msg: value,
  102. type: 1
  103. };
  104. //将object转成json字符串发送给服务端
  105. var json = JSON.stringify(object);
  106. websocket.send(json);
  107. } else {
  108. console.log('未与服务器链接.');
  109. }
  110. });
  111. })
  112. function toast(text, icon) {
  113. $.toast({
  114. text: text,
  115. heading: '新消息',
  116. icon: icon,
  117. showHideTransition: 'slide',
  118. allowToastClose: true,
  119. hideAfter: 3000,
  120. stack: 5,
  121. position: 'top-right',
  122. bgColor: '#444444',
  123. textColor: '#eeeeee',
  124. textAlign: 'left',
  125. loader: true,
  126. loaderBg: '#006eff'
  127. });
  128. }
  129. </script>
  130. </body>
  131. </html>

创建一个定时任务发送消息

  1. package socket.task;
  2. import com.alibaba.fastjson.JSONObject;
  3. import org.springframework.scheduling.annotation.Scheduled;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.web.socket.TextMessage;
  6. import socket.config.MySocketHander;
  7. import java.time.LocalDateTime;
  8. @Component
  9. public class ScheduledTask {
  10. @Scheduled(fixedRate = 3000)
  11. public void scheduledTask() {
  12. System.out.println("任务执行时间:" + LocalDateTime.now());
  13. JSONObject obj = new JSONObject();
  14. obj.put("msg", "34r35436554766787dfds");
  15. MySocketHander.sendMessageToUsers(new TextMessage(obj.toJSONString()));
  16. }
  17. }

在启动类上添加:@EnableScheduling

Spring方式实现Socket通讯 - 图1