SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用

SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用

WebSocket 简介

WebSocket 是基于TCP协议的,在第一次请求时是http方式并在请求头中携带有协议升级的参数,服务器接收到请求后会返回101状态码,表示协议升级。后续请求就走ws://方式

WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)

首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解

SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用

一、WebSocket 的应用场景

社交 / 订阅

比如微信朋友圈的实时更新提醒、点赞或评论的红点通知,比如qq的特别关注人的动态提醒,比如聊天信息的实时同步,比如新闻客户端的订阅通知等等。

多玩家游戏

对于在线实时的多人游戏,互动效率是非常重要的,你可不想在扣动扳机之后,你的对手却已经在10秒钟之前移动了位置。

协同办公 / 编辑

我们生活在分散式办公的时代,时常需要在不同地点同时编辑同一份文档,比如 office文档、编程文件等。

股市基金报价

金融界瞬息万变——几乎是每毫秒都在变化。过时的信息也只能导致损失,我们人类的大脑不能持续以那样的速度处理那么多的数据,需要一些算法来帮我们处理这些事情。当你有一个显示盘来跟踪你感兴趣的公司时,你肯定想要随时知道他们的价值,而不是10秒前的数据。使用WebSocket可以流式更新这些数据变化而不需要等待。

体育实况播放

在体育播报的体验中,减低时延是最重要的一点。

音视频聊天 / 视频会议 / 在线教育

用WebSockets getUserMedia API和HTML5音视频元素明显是个不错的选择。WebRTC的出现顺理成章的成为我刚才概括的组合体,它看起来很有希望,但其缺乏目前浏览器的支持。

基于位置的应用

越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。比如共享单车、共享汽车、地图GPS服务、疫情监控目标人的实时运动轨迹、运动员的轨迹分析。借用WebSocket TCP链接可以让数据飞起来。


介绍完上面这些,我们接下来进入代码实战

二、实战demo

Maven 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.Springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>web-socket</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>web-socket-demo</name>
    <description>webSocketDemo</description>
    <properties>
        <Java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- websocket 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <!-- 定时任务依赖-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>
        <!-- cron表达式依赖-->
        <dependency>
            <groupId>xin.altitude.cms</groupId>
            <artifactId>ucode-cms-quartz</artifactId>
            <version>1.5.4.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

三、服务端配置

1、配置

/**
 * @apiNote 创建WebSokcet配置类
 */

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

2、服务端配置

服务终端类:用java注解来监听连接@ServerEndpoint、连接成功@OnOpen、连接关闭@OnClose、收到消息等状态@OnMessage

/**
 * @date 2023/2/5
 * @apiNote  创建WebSokcet工具类
 * @deprecated 测试地址:ws://127.0.0.1:8080/websocket
 * 测试工具:http://www.jsons.cn/websocket/
 */

@Component
@ServerEndpoint(value = "/websocket")
@Deprecated
public class WebSocketServer {
    private final static Logger log = LoggerFactory.getLogger(WebSocketServer.class);

    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */

    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */

    private Session session;

    /**
     * 连接建立成功调用的方法
     */

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        //加入set中
        webSocketSet.add(this);
        //在线数加1
        addOnlineCount();
        log.info("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */

    @OnClose
    public void onClose() {
        //从set中删除
        webSocketSet.remove(this);
        //在线数减1
        subOnlineCount();
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("来自客户端的消息:" + message);

        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 发送消息的私有方法,需要调用配置中的session才能发送
     * @param message 需要发送的消息
     */

    private void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群发自定义消息
     */

    @SneakyThrows
    public static void sendInfo(String message){
        log.info(message);
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

3、实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {

    private String name;
    private Integer num;
}

4、定时任务

/**
 * @date 2023/2/5
 * @apiNote 定时任务。需要的依赖:ucode-cms-quartz、quartz
 */

@Component
@Slf4j
@DisallowConcurrentExecution
@CronExp(id = 1, cron = "0/3 * * * * ?"// 每3秒向客户端发送一次数据
public class ItemJob implements Job {

    private static ArrayList<Item> items = new ArrayList<Item>();

    static {
        items.add(new Item("Mon"12));
        items.add(new Item("Tue"8));
        items.add(new Item("Wed"16));
        items.add(new Item("Thu"2));
    }

    @SneakyThrows // 隐式处理异常
    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("定时任务被执行了。。。");
        items.forEach(e -> e.setNum((int) (Math.random() * 100)));
        Map<String, Integer> map = EntityUtils.toMap(items, Item::getName, Item::getNum);
        String result = JSON.toJSONString(map);
        WebSocketServer.sendInfo(result);
    }
}

5、启动类

package com.exmaple;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @date 2023/2/5
 * @apiNote 启动类
 */

@SpringBootApplication
public class WebSocketDeomApp {

    public static void main(String[] args) {
        SpringApplication.run(WebSocketDeomApp.classargs);
    }
}

四、客户端(前端)配置

客户端采用Vue框架和element-ui,模拟大屏实时数据展示效果。

展示的样式效果有:线形进度条、仪表盘、环形进度条。此外点击按钮还能想服务的发送当前页面展示的第一条数据,需要发送所有数据可以自行配置。

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket 客户端</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/sockjs-client/0.3.4/sockjs.min.js"></script>
    <script src="https://cdn.staticfile.org/vue/2.6.12/vue.min.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <link href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" rel="stylesheet"/>
</head>
<body>

<div id="bart" style="width: 50%;text-align:center;margin:100px auto;">
    <!-- 线形进度条 -->
    <el-progress :text-inside="true" :stroke-width="26" :percentage=percentage :color="colors"></el-progress>
    <el-progress :percentage=percentage1 :color="colors"></el-progress>
    <el-progress :percentage=percentage2 :color="colors"></el-progress>
    <el-progress :percentage=percentage3 :color="colors"></el-progress>
    <br><br>
    <!-- 仪表盘-->
    <el-progress type="dashboard" :percentage="percentage" :color="colors"></el-progress>
    <el-progress type="dashboard" :percentage=percentage1 :color="colors">></el-progress>
    <!-- 环形进度条-->
    <el-progress type="circle" :percentage=percentage2 :color="colors"></el-progress>
    <el-progress type="circle" :percentage=percentage3></el-progress>
    <el-result :icon=iconFlag title="提示" subTitle="请根据提示进行操作" style="padding: 10px 0px;">
    </el-result>

    <br>
    <!-- 向服务端发送消息-->
    <el-button type="primary" @click="send()" round>发送当前值</el-button>
</div>


<script>
    new Vue({
        el'#bart',
        data() {
            return {
                iconFlag'success',
                percentage30,
                percentage130,
                percentage230,
                percentage330,
                colors: [
                    {color'#f56c6c'percentage20},
                    {color'#e6a23c'percentage40},
                    {color'#5cb87a'percentage60},
                    {color'#1989fa'percentage80},
                    {color'#6f7ad3'percentage100}
                ],
                ws""
            }
        },
        watch: {
            percentage: {
                handler(newName, oldName) {
                    if (newName > 70) {
                        this.iconFlag = 'success'
                    } else if (newName > 40) {
                        this.iconFlag = 'warning'
                    } else {
                        this.iconFlag = 'error'
                    }
                }
            }
        },
        created() {
            this.init()
        },
        methods: {
            format(percentage) {
                console.log("percentage值", percentage)
                return percentage > 100 ? '满' : `${percentage}%`;
            },
            // 向服务端发送消息
            send() {
                ws.send("当前值为:" + this.percentage)
            },
            async init() {
                that = this
                // 检测浏览器是否支持ws
                if ('WebSocket' in window) {
                    console.log('创建websocket');
                    ws = new WebSocket("ws://localhost:8080/websocket");
                    this.ws = ws
                } else {
                    alert("该浏览器不支持websocket!");
                }

                ws.onopen = function (event{
                    console.log('建立链接');
                    ws.send("Hello WebSockets!");
                    that.ws = ws
                }

                ws.onclose = function (event{
                    console.log('连接关闭');
                    ws.close();
                }
                ws.onmessage = function (event{
                    var res = event.data
                    if (res.search("Thu") == -1) {
                        console.log('收到消息:', res, '类型:'typeof res);
                    } else {
                        var parse = JSON.parse(res);
                        that.percentage = parse['Thu']
                        that.percentage1 = parse['Tue']
                        that.percentage2 = parse['Wed']
                        that.percentage3 = parse['Mon']
                    }
                }
            }
        }
    })
</script>
</body>
</html>

五、展示效果


SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用


Tips:想要获取文章中可直接运行demo的源码,只需关注本公众号发送「WebSocket源码」即可获得。

扫描关注
SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用

原文始发于微信公众号(随笔闲谈):SpringBoot+WebSocket实战demo,搭建实时大屏展示功能,拿来即可用

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

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/251110.html

(0)
软考助手的头像软考助手

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!