• Springboot整合Websocket


    目录

    一.Websocket介绍

    1.Websocket简介

    2.Websocket与http的区别

    3.为什么要用Websocket

    二.Springboot整合Websocket

    1.引入maven依赖

    2.新建Websocket配置类

    3.新建Websocket的处理类

    4.消息推送后端代码

    5.前端代码

    6.结果


    一.Websocket介绍

    1.Websocket简介

            Web Sockets的目标是在一个单独的持久连接上提供全双工、双向通信。在JavaScript中创建了WebSocket之后。会有一个HTTP请求发送到浏览器以发起连接。在取得服务器响应后,建立的连接会从HTTP升级为Web Socket协议(ws)。

    2.Websocket与http的区别

    3.为什么要用Websocket

            我们想要查询当前的排队情况,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)

    websocket的优点

    1.支持双向通信,实时性更强 

    2.更好的二进制支持

    3.较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较
    小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户
    端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头
    部。

    4.支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定
    义压缩算法等)

    总结就是::支持双向通信,更灵活,更高效,可扩展性更好

    二.Springboot整合Websocket

    1.引入maven依赖

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

    2.新建Websocket配置类

    1. /**
    2. * websocket
    3. * 的配置信息
    4. */
    5. @Configuration
    6. public class WebSocketConfig {
    7. @Bean
    8. public ServerEndpointExporter serverEndpointExporter() {
    9. return new ServerEndpointExporter();
    10. }
    11. }

    3.新建Websocket的处理类

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

    4.消息推送后端代码

    在自己的Controller写个方法调用WebSocketServer.sendInfo()。
    程序中使用定任务不停的向客户端发送消息

    1. @Controller
    2. @RequestMapping("/api/test")
    3. @Api(description = "服务器向客户端推送消息接口", tags = "Test")
    4. public class TestController {
    5. @Autowired
    6. private TestServiceImpl testServiceImpl;
    7. /**
    8. * 启动页面
    9. * @return
    10. */
    11. @GetMapping("/start")
    12. public String start(){
    13. return "index";
    14. }
    15. @PostMapping("/pushToWeb")
    16. @ApiOperation(value = "服务器端向客户端推送消息", notes = "服务器端向客户端推送消息")
    17. public ResponseBean pushToWeb(@RequestBody @ApiParam(value = "回收人编码和医院编码", required = true) CodesInfo info){
    18. testServiceImpl.printTime();
    19. return new ResponseBean<>(200, "success", "123456");
    20. }
    21. }
    1. @Service
    2. @EnableScheduling
    3. public class TestServiceImpl {
    4. //打印时间
    5. @Scheduled(fixedRate=1000) //1000毫秒执行一次
    6. public void printTime(){
    7. SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    8. String date = dateFormat.format(new Date());
    9. WebSocketServer.sendInfo(date,"10");
    10. System.out.println(date);
    11. }
    12. }

    5.前端代码

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>websocket通讯title>
    6. head>
    7. <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js">script>
    8. <script>
    9. let socket;
    10. function openSocket() {
    11. const socketUrl = "ws://localhost:9091/api/pushMessage/" + $("#userId").val();
    12. console.log(socketUrl);
    13. if(socket!=null){
    14. socket.close();
    15. socket=null;
    16. }
    17. socket = new WebSocket(socketUrl);
    18. //打开事件
    19. socket.onopen = function() {
    20. console.log("websocket已打开");
    21. };
    22. //获得消息事件
    23. socket.onmessage = function(msg) {
    24. console.log(msg.data);
    25. //发现消息进入,开始处理前端触发逻辑
    26. };
    27. //关闭事件
    28. socket.onclose = function() {
    29. console.log("websocket已关闭");
    30. };
    31. //发生了错误事件
    32. socket.onerror = function() {
    33. console.log("websocket发生了错误");
    34. }
    35. }
    36. function sendMessage() {
    37. socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
    38. console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
    39. }
    40. script>
    41. <body>
    42. <p>【socket开启者的ID信息】:<div><input id="userId" name="userId" type="text" value="10">div>
    43. <p>【客户端向服务器发送的内容】:<div><input id="toUserId" name="toUserId" type="text" value="20">
    44. <input id="contentText" name="contentText" type="text" value="hello websocket">div>
    45. <p>【操作】:<div><a onclick="openSocket()">开启socketa>div>
    46. <p>【操作】:<div><a onclick="sendMessage()">发送消息a>div>
    47. body>
    48. html>

    6.结果

     

  • 相关阅读:
    PySpark的存储不同格式文件
    形式化定义软件动态更新
    深度学习 Pytorch笔记 B站刘二大人 梯度下降算法 Gradient-Descend 数学推导与源码实现 (2/10)
    Java线程stop,sleep,yield,join
    startsWith()方法的使用
    SQL必知会(一)-学习前的准备
    网络练习题判断带答案
    Mybatis Plus 多租户id使用
    离散数学22_第8章图__握手定理
    如何将 Python 运用到实际的测试工作中
  • 原文地址:https://blog.csdn.net/qq_45443475/article/details/127442368