Springboot-接入WebSocket服务

这篇具有很好参考价值的文章主要介绍了Springboot-接入WebSocket服务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、依赖引入 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2、启动类添加bean

public class Application {

    /**
     * 会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     * 要注意,如果使用独立的servlet容器,
     * 而不是直接使用springboot的内置容器,
     * 就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}    

3、websocket服务创建

1、注解@ServerEndpoint("/client/websocket/{deviceId}")

2、地址参数与restful 风格一致

3、方法上通过获取地址参数 @PathParam( value = "deviceId")

4、方法getRemoteAddress() 可以获取客户端IP,如果是本机请求 则返回0.0.0.0.0.1

5、只能通过本地缓存对象sessionMap 存储session信息。

6、如果需要集群、分布式,则使用Nginx 做负载均衡(IP hash)

7、如果需要bean注入其他对象,必须使用构造函数手动申明SpringUtils.getBean(RedisCache.class);


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; 
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 与客户端进行socket通信服务
 * @author xuancg
 */
@ServerEndpoint("/client/websocket/{deviceId}")
@Component
@Slf4j
public class ClientSocketService {

    private RedisCache redisCache;
    private ClientProperites clientProperites; 
    /** 用于存储当前服务器连接的socket  deviceId-session */
    private static Map<Long, Session> sessionMap = new ConcurrentHashMap<>(32);


    /**
     * 必须通过构造函数引入bean
     */
    public ClientSocketService(){
        this.redisCache = SpringUtils.getBean(RedisCache.class);
        this.clientProperites = SpringUtils.getBean(ClientProperites.class); 
        log.info("websocket准备完成");
    } 

    /**
     * 连接事件,加入注解
     * @param deviceId
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam( value = "deviceId") Long deviceId, Session session ) {
        // 设置消息体最大大小及session空闲时间
        int MAX_MESSAGE_SIZE 2 * 1024 * 1024;
        session.setMaxTextMessageBufferSize(MAX_MESSAGE_SIZE);
        session.setMaxBinaryMessageBufferSize(MAX_MESSAGE_SIZE);
        session.setMaxIdleTimeout(1 * 1000 * 60);
        log.info("客户端发起连接deviceId={}", deviceId);
       
    }

    /**
     * 连接事件,加入注解
     * 用户断开链接
     * 此处不允许执行删除sessionMap操作。由于deviceId 可能是恶意构造,需要做其他参数,
        或者请求token 验证,或者通过接收消息关闭onMessage
     * @param deviceId
     * @param session
     */
    @OnClose
    public void onClose(@PathParam ( value = "deviceId") Long deviceId, Session session ) {
        log.info("客户端关闭连接deviceId={}", deviceId);
        close(session);
    }

    /**
     * 当接收到用户上传的消息
     * @param deviceId
     * @param session
     */
    @OnMessage
    public void onMessage(@PathParam ( value = "deviceId") Long deviceId, Session session ,String message) {
        log.info("接收客户端请求 deviceId=,message={}", deviceId, message);
         
    }
  


    /**
     * 给单个用户推送消息
     * @param session
     * @param message
     */
    private void sendMessage(Session session, ClientNotifyResp message){
        if(session == null){
            return;
        }
        // 同步
        RemoteEndpoint.Async async = session.getAsyncRemote();
        async.sendText(JSONUtil.toJsonStr(message));
    }

    /**
     * 处理用户活连接异常
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throwable.printStackTrace();
    }

    private void close(Session session){
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getRemoteAddress(Session session) {
        if (session == null) {
            return null;
        }
        RemoteEndpoint.Async async = session.getAsyncRemote();

        //在Tomcat 8.0.x版本有效
        //InetSocketAddress addr0 = (InetSocketAddress) getFieldInstance(async,"base#sos#socketWrapper#socket#sc#remoteAddress");
        //System.out.println("clientIP0" + addr0);
        //在Tomcat 8.5以上版本有效
        Object obj = getFieldInstance(async, "base#socketWrapper#socket#sc#remoteAddress");
        if(null == obj){
            return "127.0.0.1";
        }
        InetSocketAddress addr = (InetSocketAddress) obj;
        String ip = addr.toString().replace("/", "");
        int idx = ip.lastIndexOf(":");
        if(idx > 0){
            return ip.substring(0, idx);
        }
        return ip;
    }

    private static Object getFieldInstance(Object obj, String fieldPath) {
        String fields[] = fieldPath.split("#");
        for (String field : fields) {
            obj = getField(obj, obj.getClass(), field);
            if (obj == null) {
                return null;
            }
        }

        return obj;
    }

    private static Object getField(Object obj, Class<?> clazz, String fieldName) {
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                Field field;
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(obj);
            } catch (Exception e) {
            }
        }

        return null;
    }



}

4、拦截器放行

或者添加自定义拦截器文章来源地址https://www.toymoban.com/news/detail-830209.html

httpSecurity
// CSRF禁用,因为不使用session TODO
.csrf().disable()
// 认证失败处理类
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
// 过滤请求
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/client/websocket/**").permitAll()

5、客户端调用demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Java后端WebSocket的Tomcat实现</title>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body>

<script type="text/javascript">
    var websocket = null;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket('ws://localhost:8080/mood-service/client/websocket/200013');
    }
    else {
        alert('当前浏览器 Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("WebSocket连接发生错误");
    };

    //连接成功建立的回调方法
    websocket.onopen = function () {
        setMessageInnerHTML("WebSocket连接成功");
    }

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("WebSocket连接关闭");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        closeWebSocket();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭WebSocket连接
    function closeWebSocket() {
        websocket.close();
    }

    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

到了这里,关于Springboot-接入WebSocket服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot进阶(49):SpringBoot之集成WebSocket实现前后端通信 | 超级详细,建议收藏

            在上一期,我对WebSocket进行了基础及理论知识普及学习,WebSocket是一种基于TCP协议实现的全双工通信协议,使用它可以实现实时通信,不必担心HTTP协议的短连接问题。Spring Boot作为一款微服务框架,也提供了轻量级的WebSocket集成支持,本文将介绍如何在Spring Boot项

    2024年02月14日
    浏览(40)
  • WebSocket服务端数据推送及心跳机制(Spring Boot + VUE)

    一、WebSocket简介 HTML5规范在传统的web交互基础上为我们带来了众多的新特性,随着web技术被广泛用于web APP的开发,这些新特性得以推广和使用,而websocket作为一种新的web通信技术具有巨大意义。WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双

    2024年02月12日
    浏览(39)
  • 服务端(后端)主动通知前端的实现:WebSocket(springboot中使用WebSocket案例)

    我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主流实现方式: 轮询:缺点很多,但是实现简单 websocket:在浏览器和服务器之间建立 tcp 连接,实现全双工通信 springboot 使用 websocket 有

    2023年04月14日
    浏览(66)
  • Spring Boot 集成 WebSocket 实现服务端推送消息到客户端

          假设有这样一个场景:服务端的资源经常在更新,客户端需要尽量及时地了解到这些更新发生后展示给用户,如果是 HTTP 1.1,通常会开启 ajax 请求询问服务端是否有更新,通过定时器反复轮询服务端响应的资源是否有更新。                         在长时间不更新

    2024年02月16日
    浏览(55)
  • Spring Boot进阶(49):实时通信不再是梦想,SpringBoot+WebSocket助你轻松实现前后端即时通讯!

            在上一期,我对WebSocket进行了基础及理论知识普及学习,WebSocket是一种基于TCP协议实现的全双工通信协议,使用它可以实现实时通信,不必担心HTTP协议的短连接问题。Spring Boot作为一款微服务框架,也提供了轻量级的WebSocket集成支持,本文将介绍如何在Spring Boot项

    2024年02月11日
    浏览(48)
  • 【微服务】Spring Boot集成ELK实用案例

    推荐一款我一直在用国内很火的 AI网站 ,包含 GPT3.5/4.0、文心一言、通义千问、智谱AI等多个AI模型 ,支持PC、APP、VScode插件同步使用,点击链接跳转-ChatGPT4.0中文版 在现代软件开发中,微服务架构已成为一种流行趋势。随之而来的挑战之一是如何有效地管理和分析分布在各个

    2024年02月20日
    浏览(35)
  • 【项目实战】基于netty-websocket-spring-boot-starter实现WebSocket服务器长链接处理

    项目中需要建立客户端与服务端之间的长链接,首先就考虑用WebSocket,再来SpringBoot原来整合WebSocket方式并不高效,因此找到了netty-websocket-spring-boot-starter 这款脚手架,它能让我们在SpringBoot中使用Netty来开发WebSocket服务器,并像spring-websocket的注解开发一样简单 2.1.1 @ServerEndpo

    2024年02月12日
    浏览(46)
  • 【SpringBoot】Spring Boot 单体应用升级 Spring Cloud 微服务

    Spring Cloud 是在 Spring Boot 之上构建的一套微服务生态体系,包括服务发现、配置中心、限流降级、分布式事务、异步消息等,因此通过增加依赖、注解等简单的四步即可完成 Spring Boot 应用到 Spring Cloud 升级。 Spring Boot 应用升级为 Spring Cloud Cloud Native 以下是应用升级 Spring Clou

    2024年02月02日
    浏览(43)
  • Spring/Spring boot项目接入traceId

    分布式系统中,由多个服务构成,每个请求路由过来后,会在多个服务中留下追踪ID,可以基于此追踪ID排查问题,分析请求的执行链路。 业界也有比较成熟的链路追踪ID方案,比如Skywalking,它基于动态字节码技术,本身会增加系统的复杂性,以及它需要单独部署服务对信息

    2024年01月24日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包