SpringBoot 集成 WebSocket(1)

简介: SpringBoot 集成 WebSocket

前言

       最近在做一个 WebSocket 通信服务的软件,所以必须跟着学一学。

1、WebSocket 概述

       一般情况下,我们的服务器和服务器之间可以发送请求,但是服务器是不能向浏览器去发送请求的。因为设计之初并没有想到以后会出现服务端频繁向客户端发送请求的情况。

       全双工的通信协议(WebSocket 最大的特点是浏览器也可以往服务器发请求,服务器也可以向浏览器发请求)。

1.1、浏览器和服务器使用WebSocket通信流程

1. 浏览器发起http请求,请求建立 WebSocket 连接

这里的协议升级就是说,我想通过这个 http 连接去升级为 WebSocket 连接

2. 服务器响应统一协议更改

3. 相互发送数据

升级了协议之后浏览器就可以和服务器相互通信了:

1.2、总结

  • WebSocket 协议是建立在 tcp 协议基础上的,所以不同语言也都支持
  • tcp 协议是全双工协议,http 协议基于它是单向的
  • WebSocket 没有同源限制,所以前后端端口不一致也不影响信息的发送

2、Java 实现 WebSocket 的两种方式

2.1、基于注解实现WebSocket服务器端

服务终端类

  • @ServerEndpoint:监听连接(需要传递一个地址参数)
  • @OnOpen:连接成功
  • @OnClose:连接关闭
  • @OnMessage:收到消息

配置类

  • 把 Spring 中的 ServerEndpointExporter 对象注入进来

2.2.1、编写服务终端类

// 监听哪些客户端来连接了WebSocket服务端
// 监听websocket地址 /myWs
@ServerEndpoint("/myWs")
@Component
@Slf4j
public class WebServerEndpoint {
 
    // 因为可能有多个客户端所以这里需要保证线程安全
    static Map<String,Session> sessionMap = new ConcurrentHashMap<>();
 
    // 建立连接时执行的操作
    @OnOpen
    public void onOpen(Session session){   // 每个websocket连接对于服务端来说都是一个Session
        sessionMap.put(session.getId(),session);
        log.info("websocket is open");
    }
 
    /**
     * 收到客户端消息时执行的操作
     * @param text 接受到客户端的消息
     * @return 返回给客户端的消息
     */
    @OnMessage
    public String onMessage(String text){
        log.info("收到一条新消息: " + text);
        return "收到 !";
    }
 
    // 连接关闭时执行的操作
    @OnClose
    public void onClose(Session session){
        sessionMap.remove(session.getId());
        log.info("websocket is close");
    }
 
    @Scheduled(fixedRate = 2000) // 每隔2s执行一次
    public static void sendMessage() throws IOException {
        for(String key: sessionMap.keySet()){ // 给所有客户端发送消息
            sessionMap.get(key).getBasicRemote().sendText("beat");
        }
    };
}

注意:这里监听的地址不可以是 "ws" 不然会报错,可能这是关键字吧,毕竟我们的协议就叫 ws 。

2.2.2、编写配置类

// 需要注入Bean的话必须声明为配置类
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

2.2、HTML + JS 实现客户端

在 resources 目录下创建 static/ws-client.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket Client</title>
</head>
<script>
    // 客户端和服务器连接的地址(我们服务端监听的地址)
    let ws = new WebSocket("ws://localhost:8080/myWs1")
    ws.onopen=function (){ // 连接打开的时候向服务器发送一条消息
        ws.send("hey man")
    }
    ws.onmessage=function (message) {
        console.log(message.data)
    }
</script>
<body>
<h1>WebSocket</h1>
</body>
</html>

测试:启动 SpringBoot 并访问  localhost:8080/ws-client.html

执行结果:

2.3、基于 Spring 框架实现 WebSocket 服务器端

Spring 提供的类和框架

  • HttpSessionHandshakeInterceptor(抽象类):握手拦截器,在握手前后添加操作
  • AbstractWebSocketHandler(抽象类):WebSocket 处理程序,监听连接前、中、后
  • WebSocketConfigurer(接口):配置程序,比如配置监听哪个端口,配置自定义的握手拦截器,配置我们自定义的处理程序

2.3.1、编写握手拦截器类

/**
 * WebSocket 自定义握手拦截器
 */
@Component
@Slf4j
public class MyInterceptor extends HttpSessionHandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        log.info("握手前");
        log.info("远程地址 => " + request.getRemoteAddress());
        // 保留父类的操作
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }
 
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
        log.info("完成握手");
        // 完成握手后它就会把 HTTP 协议升级为 WebSocket 协议
        super.afterHandshake(request, response, wsHandler, ex);
    }
}

2.3.2、编写 WebSocket 处理程序

/**
 * WebSocket 自定义处理程序
 */
@Slf4j
@Component
public class MyWsHandler extends AbstractWebSocketHandler {
    // WebSocketSession 对象可以封装一下吧用户的信息封装进去
    private static Map<String, SessionBean> sessionMap = new ConcurrentHashMap<>();
    // 线程安全的int值
    private static AtomicInteger clientIdMaker = new AtomicInteger(0);
 
    // 连接建立
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        super.afterConnectionEstablished(session);
        // 放在父方法调用之后
        SessionBean sessionBean = new SessionBean(session, clientIdMaker.getAndIncrement());
        sessionMap.put(session.getId(),sessionBean);
        log.info(sessionMap.get(session.getId()) + " connected");
    }
 
    // 收到消息
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        log.info(sessionMap.get(session.getId()).getClientId() + " : " + message.getPayload());
    }
 
    // 传输异常
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        super.handleTransportError(session, exception);
        // 如果异常就关闭 session
        if (session.isOpen())
            session.close();
        sessionMap.remove(session.getId());
    }
 
    // 连接关闭
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        log.info(sessionMap.get(session.getId()) + " closed");
        sessionMap.remove(session.getId());
    }
 
    @Scheduled(fixedRate = 2000) // 每隔2s执行一次
    public static void sendMessage() throws IOException {
        for(String key: sessionMap.keySet()){ // 给所有客户端发送消息
            sessionMap.get(key).getWebSocketSession().sendMessage(new TextMessage("beat"));
        }
    }
}

2.3.3、编写配置类

@Configuration
@EnableWebSocket    // 启用 spring 提供的 websocket 功能
public class MyWsConfig implements WebSocketConfigurer {
 
    @Resource
    MyWsHandler myWsHandler; // 引入我们在MyWsHandler上声明的Bean(@Component)
    @Resource
    MyInterceptor myInterceptor;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 监听的地址
        registry.addHandler(myWsHandler,"/myWs1")
                .addInterceptors(myInterceptor)
                .setAllowedOrigins("*"); // 允许的源
    }
}

修改 html 中的 websocket 地址为 /myWs1

测试:访问 localhost:8080/ws-client.html

3、总结

服务器会和每个客户端维护一个连接 :

3、WebSocket 实现多人聊天室

3.1、需求

  • 进入聊天室
  • 群聊功能,任何人说话,所有人都能接受到消息
  • 退出群聊

SpringBoot 集成 WebSocket(2)https://developer.aliyun.com/article/1534234

相关文章
|
3月前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
842 1
|
3天前
|
监控 Java Nacos
使用Spring Boot集成Nacos
通过上述步骤,Spring Boot应用可以成功集成Nacos,利用Nacos的服务发现和配置管理功能来提升微服务架构的灵活性和可维护性。通过这种集成,开发者可以更高效地管理和部署微服务。
48 17
|
3天前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
31 11
|
5天前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
106 12
|
11天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
51 8
|
1月前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
|
2月前
|
消息中间件 Java Kafka
什么是Apache Kafka?如何将其与Spring Boot集成?
什么是Apache Kafka?如何将其与Spring Boot集成?
86 5
|
2月前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
71 1
|
2月前
|
缓存 监控 前端开发
Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信
本文探讨了在 Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信。首先介绍了 WebSocket 和 Socket.IO 的基本概念及其优势,接着详细讲解了 Go 语言中 WebSocket 的实现方法,以及二者集成的重要意义和具体步骤。文章还讨论了集成过程中需要注意的问题,如协议兼容性、消息格式、并发处理等,并提供了实时聊天、数据监控和在线协作工具等应用案例,最后提出了性能优化策略,包括数据压缩、缓存策略和连接管理优化。旨在帮助开发者更好地理解并应用这些技术。
122 3
|
2月前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
483 1