当前位置: 代码网 > it编程>编程语言>Java > WebSocket详解(Java后端,基于Springboot)

WebSocket详解(Java后端,基于Springboot)

2024年08月02日 Java 我要评论
WebSocket是一种在单个TCP连接上进行全双工通信的协议。这种协议在2011年被IETF定为标准RFC 6455,并由RFC7936补充规范,同时WebSocket API也被W3C定为标准。它使得客户端和服务器之间的数据交换变得更加简单,特别是允许服务端主动向客户端推送数据。实时性更高:WebSocket技术可以实现实时通信,传输数据的延迟更低,因此可以更快地将数据传输到客户端。减少网络带宽的使用:通过建立一个持久化的连接,WebSocket减少了HTTP协议中每次请求和响应所需要的网络带宽。

websocket的介绍

websocket是一种在单个tcp连接上进行全双工通信的协议。这种协议在2011年被ietf定为标准rfc 6455,并由rfc7936补充规范,同时websocket api也被w3c定为标准。它使得客户端和服务器之间的数据交换变得更加简单,特别是允许服务端主动向客户端推送数据。

websocket技术具有一系列显著特点:

  1. 实时性更高:websocket技术可以实现实时通信,传输数据的延迟更低,因此可以更快地将数据传输到客户端。
  2. 减少网络带宽的使用:通过建立一个持久化的连接,websocket减少了http协议中每次请求和响应所需要的网络带宽。
  3. 双向通信:websocket技术支持全双工通信模式,即客户端和服务器可以同时发送和接收数据。
  4. 跨域支持:websocket技术可以跨不同的域进行通信。
  5. 安全性更高:websocket可以通过ssl/tls协议实现加密通信,确保数据传输的安全性。

websocket的应用场景非常广泛,包括但不限于:

  • 即时通讯:如聊天应用、在线客服系统等,用户可以实时地发送和接收消息。
  • 实时数据展示:如实时股票行情、实时天气更新等,可以实时地推送数据给前端。
  • 多人游戏:websocket可以实现多人在线游戏,玩家可以实时地进行交互和通信。
  • 实时协作:如实时协同编辑器,多个用户可以同时编辑一个文档,并实时地看到其他用户的操作。
  • 数据监控:websocket可以用于实时监控系统的运行状态、日志更新等,便于及时发现和解决问题。

注意,websocket也存在一些潜在的问题,如兼容性问题(特别是在一些旧版本的浏览器上),服务器资源占用(由于需要维护大量的长连接),以及安全性问题(需要特殊的安全设置以防止恶意攻击和数据泄漏)。

1. 添加依赖(基于idea)

首先,你需要在你的pom.xml(如果你使用maven)或build.gradle(如果你使用gradle)中添加spring boot websocket的依赖。

对于maven,添加以下依赖:

<dependencies>  
    <!-- spring boot starter websocket -->  
    <dependency>  
        <groupid>org.springframework.boot</groupid>  
        <artifactid>spring-boot-starter-websocket</artifactid>  
    </dependency>  
    <!-- 如果你还需要http功能 -->  
    <dependency>  
        <groupid>org.springframework.boot</groupid>  
        <artifactid>spring-boot-starter-web</artifactid>  
    </dependency>  
    <!-- 其他依赖... -->  
</dependencies>

对于gradle,添加以下依赖:

dependencies {  
    // spring boot starter websocket  
    implementation 'org.springframework.boot:spring-boot-starter-websocket'  
    // 如果你还需要http功能  
    implementation 'org.springframework.boot:spring-boot-starter-web'  
    // 其他依赖...  
}

2. 配置websocket

接下来,你需要配置websocket端点。我们继承webmvcconfigurationsupport。以下是一个简单的配置示例:


import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.web.servlet.config.annotation.resourcehandlerregistry;
import org.springframework.web.servlet.config.annotation.webmvcconfigurationsupport;
import org.springframework.web.socket.server.standard.serverendpointexporter;

/**
 * 开启websocket支持 配置websocket并开启
 */
@configuration
public class websocketconfig extends webmvcconfigurationsupport {
    @bean
    public serverendpointexporter serverendpointexporter() {
        return new serverendpointexporter();
    }

    @override
    protected void addresourcehandlers(resourcehandlerregistry registry) {
        super.addresourcehandlers(registry);
        // 相对路径
        registry.addresourcehandler("image/**").addresourcelocations("classpath:/static/image/");
        // 绝对路径
        // registry.addresourcehandler("image/**").addresourcelocations("file:" + "image/");
    }
}

上述代码的websocketconfig该类继承自webmvcconfigurationsupport,并试图同时配置websocket和静态资源处理。然而,这种设计是有问题的,原因如下:

  1. 继承webmvcconfigurationsupport: 继承webmvcconfigurationsupport会关闭spring boot的自动配置功能,这意味着您将失去很多spring boot为mvc提供的默认配置。通常,如果您只是想要添加或覆盖某些配置,而不是完全自定义整个mvc配置,那么不推荐继承webmvcconfigurationsupport

  2. websocket配置: 在这个配置类中,@bean注解创建一个serverendpointexporter的bean,这通常用于配置jsr 356 (java api for websocket) 的端点。但是,serverendpointexporter并不是spring boot的websocket消息代理的一部分,而是用于jsr 356 websocket端点的导出。如果您打算使用spring的websocket消息代理(即stomp over websocket),那么您不需要serverendpointexporter

  3. 静态资源处理: 通过覆盖addresourcehandlers方法来配置静态资源的位置。尽管这本身不是错误的,但如果您只是想要配置静态资源,那么您并不需要继承webmvcconfigurationsupport。您可以直接在另一个配置类中通过实现webmvcconfigurer接口来完成这个操作。

如果您打算使用spring的websocket消息代理,那么您应该使用类似您之前的websocketconfig配置类(不继承webmvcconfigurationsupport),并实现websocketmessagebrokerconfigurer接口。静态资源处理则可以通过实现webmvcconfigurer接口来完成。

以下是一个改进后的配置示例:

websocket配置 (不使用webmvcconfigurationsupport)

import org.springframework.context.annotation.configuration;  
import org.springframework.messaging.simp.config.messagebrokerregistry;  
import org.springframework.web.socket.config.annotation.*;  
  
// 使用@configuration注解来标识这个类是一个配置类  
@configuration  
// 使用@enablewebsocketmessagebroker注解来启用websocket消息代理支持  
@enablewebsocketmessagebroker  
public class websocketconfig implements websocketmessagebrokerconfigurer {  
  
    @override  
    public void configuremessagebroker(messagebrokerregistry config) {  
        // 配置消息代理的前缀,客户端订阅消息时需要使用这个前缀  
        config.enablesimplebroker("/topic");  
        // 配置应用目标前缀,客户端发送消息到服务器时使用这个前缀  
        config.setapplicationdestinationprefixes("/app");  
    }  
  
    @override  
    public void registerstompendpoints(stompendpointregistry registry) {  
        // 注册一个stomp协议的websocket端点,客户端将连接到这个端点  
        // addendpoint方法第一个参数是端点url,第二个参数是使用的子协议,这里使用sockjs  
        registry.addendpoint("/websocket").withsockjs();  
    }  
  
    // (可选)配置跨域策略  
    @override  
    public void configurewebsockettransport(websockettransportregistration registration) {  
        registration.setmessagesizelimit(8192); // 设置消息大小限制  
        registration.setsendbuffersizelimit(16384); // 设置发送缓冲区大小限制  
    }  
}

静态资源配置 (通过实现webmvcconfigurer)

@configuration  
public class webmvcconfig implements webmvcconfigurer {  
  
    @override  
    public void addresourcehandlers(resourcehandlerregistry registry) {  
        registry.addresourcehandler("image/**")  
                .addresourcelocations("classpath:/static/image/");  
        // 如果需要绝对路径,可以添加另一个资源处理器  
    }  
}

websocketcontroller.java

import org.springframework.beans.factory.annotation.autowired;  
import org.springframework.messaging.handler.annotation.*;  
import org.springframework.stereotype.controller;  
  
// 使用@controller注解来标识这个类是一个控制器  
@controller  
public class websocketcontroller {  
  
    // 假设有一个服务来处理消息逻辑  
    @autowired  
    private messageservice messageservice;  
  
    // 使用@messagemapping注解定义客户端发送消息到服务器的路由  
    @messagemapping("/hello")  
    // 使用@sendto注解定义服务器向客户端发送消息的路由  
    @sendto("/topic/greetings")  
    public string greeting(string message) {  
        // 调用服务来处理消息  
        string processedmessage = messageservice.processmessage(message);  
        // 返回处理后的消息给客户端  
        return processedmessage;  
    }  
  
    // (可选)定义一个方法处理连接事件  
    @eventhandler  
    public void handleconnect(sessionconnectedevent event) {  
        // 处理客户端连接事件  
    }  
  
    // (可选)定义一个方法处理断开连接事件  
    @eventhandler  
    public void handledisconnect(sessiondisconnectevent event) {  
        // 处理客户端断开连接事件  
    }  
}

messageservice.java

import org.springframework.stereotype.service;  
  
// 使用@service注解来标识这个类是一个服务类  
@service  
public class messageservice {  
  
    public string processmessage(string message) {  
        // 这里可以添加消息处理的逻辑,比如格式化、验证等  
        // 返回一个处理后的消息  
        return "hello, " + message + "!";  
    }  
}

以上代码配置了一个简单的websocket服务器,包括websocket消息代理、端点配置以及消息处理器。

  • websocketconfig 类是websocket的配置类,通过实现 websocketmessagebrokerconfigurer 接口,定义了消息代理和端点的配置。
  • websocketcontroller 类是websocket的控制器,使用 @messagemapping 注解来映射客户端发送的消息,并使用 @sendto 注解来指定消息发送的路由。
  • messageservice 类是一个简单的服务类,用来处理websocket接收到的消息。

3.基于@serverendpoint的注解实现websocket通信

在spring框架中,@serverendpoint注解用于声明一个websocket端点,这个端点可以被客户端通过websocket协议进行连接。然而,@serverendpoint注解本身并不与spring容器集成,所以直接使用@component注解来将其声明为一个spring管理的bean是不起作用的。

要解决这个问题,你需要使用serverendpointexporter来手动注册@serverendpoint注解的类,以便它们能够被spring容器管理。serverendpointexporter是一个spring boot组件,它会自动注册带有@serverendpoint注解的类,使其可以作为websocket端点。

以下是如何使用@serverendpoint@component注解,并通过serverendpointexporter来实现websocket长连接的步骤:

1.创建一个带有@serverendpoint注解的类,用于处理websocket连接。

import javax.websocket.onclose;  
import javax.websocket.onmessage;  
import javax.websocket.onopen;  
import javax.websocket.session;  
import javax.websocket.server.serverendpoint;  
import org.springframework.stereotype.component;  
  
@serverendpoint("/ws/{userid}/{device}")  
@component  
public class mywebsocketendpoint {  
  
    @onopen  
    public void onopen(session session, @pathparam("userid") string userid, @pathparam("device") string device) {  
        // 连接建立时的处理逻辑  
        system.out.println("client connected: " + session.getid() + " with userid=" + userid + " and device=" + device);  
    }  
  
    @onmessage  
    public void onmessage(string message, session session) {  
        // 收到消息时的处理逻辑  
        system.out.println("message received: " + message + " from session: " + session.getid());  
    }  
  
    @onclose  
    public void onclose(session session) {  
        // 连接关闭时的处理逻辑  
        system.out.println("client disconnected: " + session.getid());  
    }  
}

请注意,上面的代码中使用了@pathparam注解来提取url模板参数useriddevice。但是,@pathparam并不是javax.websocket包中的一部分,它是jax-rs规范的一部分。在标准的websocket api中,你需要在@onopen方法中手动解析路径参数。

2. 创建一个配置类来注册serverendpointexporter

import org.springframework.context.annotation.bean;  
import org.springframework.context.annotation.configuration;  
import org.springframework.web.socket.server.standard.serverendpointexporter;  
  
@configuration  
public class websocketconfig {  
  
    @bean  
    public serverendpointexporter serverendpointexporter() {  
        return new serverendpointexporter();  
    }  
}

在这个配置类中,我们创建了一个serverendpointexporter的bean,spring boot会自动扫描并注册所有带有@serverendpoint注解的类。

请注意@pathparam注解在标准的websocket api中并不适用。如果你需要处理url模板参数,你可能需要手动解析它们,或者使用spring的@requestmappinghandleradapter来创建一个自定义的websocket端点处理器。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com