• springboot实现websocket


    一、maven依赖

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

    二、websocket配置类

    1. @ServerEndpoint("/websocket/{userId}")
    2. @Component
    3. public class WebSocketServer {
    4. static Log log = LogFactory.get(WebSocketServer.class);
    5. /**
    6. * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    7. */
    8. private static int onlineCount = 0;
    9. /**
    10. * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    11. */
    12. private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap<>();
    13. /**
    14. * 与某个客户端的连接会话,需要通过它来给客户端发送数据
    15. */
    16. private Session session;
    17. /**
    18. * 接收userId
    19. */
    20. private String userId = "";
    21. /**
    22. * 连接建立成功调用的方法
    23. */
    24. @OnOpen
    25. public void onOpen(Session session, @PathParam("userId") String userId) {
    26. this.session = session;
    27. this.userId = userId;
    28. if (webSocketMap.containsKey(userId)) {
    29. webSocketMap.remove(userId);
    30. webSocketMap.put(userId, this);
    31. //加入set中
    32. } else {
    33. webSocketMap.put(userId, this);
    34. //加入set中
    35. addOnlineCount();
    36. //在线数加1
    37. }
    38. log.info("用户连接:" + userId + ",当前在线人数为:" + getOnlineCount());
    39. try {
    40. sendMessage("连接成功");
    41. } catch (IOException e) {
    42. log.error("用户:" + userId + ",网络异常!!!!!!");
    43. }
    44. }
    45. /**
    46. * 连接关闭调用的方法
    47. */
    48. @OnClose
    49. public void onClose() {
    50. if (webSocketMap.containsKey(userId)) {
    51. webSocketMap.remove(userId);
    52. //从set中删除
    53. subOnlineCount();
    54. }
    55. log.info("用户退出:" + userId + ",当前在线人数为:" + getOnlineCount());
    56. }
    57. /**
    58. * 收到客户端消息后调用的方法
    59. *
    60. * @param message 客户端发送过来的消息
    61. */
    62. @OnMessage
    63. public void onMessage(String message, Session session) {
    64. log.info("用户消息:" + userId + ",报文:" + message);
    65. //可以群发消息
    66. //消息保存到数据库、redis
    67. if (StringUtils.isNotBlank(message)) {
    68. try {
    69. //解析发送的报文
    70. JSONObject jsonObject = JSON.parseObject(message);
    71. //追加发送人(防止串改)
    72. jsonObject.put("fromUserId", this.userId);
    73. String toUserId = jsonObject.getString("toUserId");
    74. //传送给对应toUserId用户的websocket
    75. if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {
    76. log.info("请求的userId:" + toUserId + "在该服务器上");
    77. webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
    78. } else {
    79. log.error("请求的userId:" + toUserId + "不在该服务器上");
    80. //否则不在这个服务器上,发送到mysql或者redis
    81. }
    82. } catch (Exception e) {
    83. e.printStackTrace();
    84. }
    85. }
    86. }
    87. /**
    88. * @param session
    89. * @param error
    90. */
    91. @OnError
    92. public void onError(Session session, Throwable error) {
    93. log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
    94. error.printStackTrace();
    95. }
    96. /**
    97. * 实现服务器主动推送
    98. */
    99. public void sendMessage(String message) throws IOException {
    100. this.session.getBasicRemote().sendText(message);
    101. }
    102. /**
    103. * 发送自定义消息
    104. */
    105. public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {
    106. log.info("发送消息到:" + userId + ",报文:" + message);
    107. if (StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
    108. webSocketMap.get(userId).sendMessage(message);
    109. } else {
    110. log.error("用户" + userId + ",不在线!");
    111. }
    112. }
    113. public static synchronized int getOnlineCount() {
    114. return onlineCount;
    115. }
    116. public static synchronized void addOnlineCount() {
    117. WebSocketServer.onlineCount++;
    118. }
    119. public static synchronized void subOnlineCount() {
    120. WebSocketServer.onlineCount--;
    121. }
    122. }
    1. /**
    2. * 开启WebSocket支持
    3. * @author
    4. */
    5. @Configuration
    6. public class WebSocketConfig {
    7. @Bean
    8. public ServerEndpointExporter serverEndpointExporter() {
    9. return new ServerEndpointExporter();
    10. }
    11. }

    三、controller

    1. @RestController
    2. public class DemoController {
    3. @GetMapping("index")
    4. public ResponseEntity index() {
    5. return ResponseEntity.ok("请求成功");
    6. }
    7. @GetMapping("page")
    8. public ModelAndView page() {
    9. return new ModelAndView("websocket");
    10. }
    11. @RequestMapping("/push/{toUserId}")
    12. public ResponseEntity pushToWeb(String message, @PathVariable String toUserId) throws IOException {
    13. WebSocketServer.sendInfo(message, toUserId);
    14. return ResponseEntity.ok("MSG SEND SUCCESS");
    15. }
    16. }

    以上代码完成了websocket的接入,可以用在线的websocket测试网站测试

    四、在线websocket测试网站

     websocket在线测试

     1、下面是测试连接的页面

     本地调试的话,用ws://ip:port/websocket/{{userId}} 连接

    2、发送消息

     发送内容{"toUserId":"1111","body":"测试消息"},服务器受到消息后,会带上fromUserId返回

    五、 nginx配置

    如果使用了nginx,需要进行配置实现websocket协议的转发

    1. location /websocket/ {
    2. proxy_pass http://127.0.0.1:7017/websocket/;
    3. proxy_http_version 1.1;
    4. proxy_set_header Upgrade $http_upgrade;
    5. proxy_set_header Connection "upgrade";
    6. }

    上面的配置放在 server配置里

    六、遇到的问题

    本地启动项目可以正常测试,但是准备打个jar包扔到服务器的时候,使用maven install出现问题,解决方案:删除   spring-boot-starter-test 依赖和 test测试类代码,或者install时忽略test文件夹。

  • 相关阅读:
    操作系统学习笔记7 | 进程同步与合作
    Golang开发习惯:变量、常量声明&使用惯例
    基于深度学习的Deepfake检测综述
    (动手学习深度学习)第13章 计算机视觉---微调
    如何管理“问题成员”?手段要“狠”
    SSM整合-表现层与前端数据传输数据协议定义与实现
    念一句咒语 AI 就帮我写一个应用,我人麻了...
    TypeScript24:TS中的声明文件
    【ARM 裸机】模仿 STM32 驱动开发
    十大排序算法(C语言)
  • 原文地址:https://blog.csdn.net/xrq1995/article/details/126581698