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

相关文章
|
4天前
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
27 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
4天前
|
JSON Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
本文详细介绍了Swagger2的使用方法,包括在Spring Boot项目中的配置与应用。重点讲解了Swagger2中常用的注解,如实体类上的`@ApiModel`和`@ApiModelProperty`,Controller类上的`@Api`、`@ApiOperation`以及参数上的`@ApiParam`等。通过示例代码展示了如何为实体类和接口添加注解,并在页面上生成在线接口文档,实现接口测试。最后总结了Swagger的优势及其在项目开发中的重要性,提供了课程源代码下载链接供学习参考。
35 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
|
4天前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
29 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
4天前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
27 0
|
4天前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
28 0
|
4天前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
23 0
|
4天前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
22 0
|
4天前
|
Java Maven 微服务
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
19 0
|
4天前
|
前端开发 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档—— Swagger 简介
第6课介绍了在Spring Boot中集成Swagger2以展示在线接口文档的方法。随着前后端分离架构的发展,API文档成为连接前端与后端开发的重要纽带。然而,代码更新频繁导致文档难以同步维护,Swagger2解决了这一问题。通过Swagger,在线API文档不仅方便了接口调用方查看和测试,还支持开发者实时测试接口数据。本文使用Swagger 2.2.2版本,讲解如何在Spring Boot项目中导入并配置Swagger2工具,从而高效管理接口文档。
27 0
|
4天前
|
消息中间件 存储 Java
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ安装
本教程介绍ActiveMQ的安装与基本使用。首先从官网下载apache-activemq-5.15.3版本,解压后即可完成安装,非常便捷。启动时进入解压目录下的bin文件夹,根据系统选择win32或win64,运行activemq.bat启动服务。通过浏览器访问`http://127.0.0.1:8161/admin/`可进入管理界面,默认用户名密码为admin/admin。ActiveMQ支持两种消息模式:点对点(Queue)和发布/订阅(Topic)。前者确保每条消息仅被一个消费者消费,后者允许多个消费者同时接收相同消息。
27 0
微服务——SpringBoot使用归纳——Spring Boot中集成ActiveMQ——ActiveMQ安装