基于Spring封装一个websocket工具类使用事件发布进行解耦和管理

news2025/6/17 13:10:50

最近工作中,需要将原先的Http请求换成WebSocket,故此需要使用到WebSocket与前端交互。故此这边需要研究一下WebSocket到底有何优点和不可替代性:

WebSocket优点:

WebSocket 协议提供了一种在客户端和服务器之间进行全双工通信的机制,这意味着客户端和服务器可以在任何时候互相发送消息,而不需要预先建立请求。与传统的 HTTP 轮询相比,WebSocket 有以下不可替代的优点:

1. 低延迟: WebSocket 提供了真正的实时通信能力,因为它允许服务器在数据可用时立即将其推送到客户端。这比 HTTP 轮询的“询问-回答”模式更高效,轮询模式可能会引入不必要的延迟。

2. 减少网络流量: 在 HTTP 轮询中,客户端需要定期发送请求以检查更新,即使没有更新也是如此。这会产生大量冗余的 HTTP 头部信息和请求响应。相比之下,WebSocket 在建立连接后,只需要非常少的控制开销就可以发送和接收消息。

3. 持久连接: WebSocket 使用单个持久连接进行通信,而不需要为每个消息或请求重新建立连接。这减少了频繁建立和关闭连接的开销,提高了效率。

4. 双向通信: WebSocket 支持全双工通信,客户端和服务器可以同时发送消息,而不需要等待对方的响应。这对于需要快速双向数据交换的应用程序来说是非常重要的。

5. 更好的服务器资源利用: 由于 WebSocket 连接是持久的,服务器可以更有效地管理资源,而不是在每个轮询请求中重新初始化资源。

6. 协议开销小: WebSocket 消息包含非常少的协议开销,相比之下,HTTP 协议的每个请求/响应都包含了完整的头部信息。

7. 支持二进制数据: WebSocket 不仅支持文本数据,还支持二进制数据,这使得它可以用于更广泛的应用场景,如游戏、视频流和其他需要高效二进制数据传输的应用。

8. 兼容性: 尽管是较新的技术,WebSocket 已经得到了现代浏览器的广泛支持,并且可以通过 Polyfills 在不支持的浏览器上使用。

时序图:

 

这个流程图展示了以下步骤:

  1. 握手阶段:客户端向服务器发送 WebSocket 连接请求,服务器响应并切换协议。
  2. 连接建立:WebSocket 连接建立后,客户端和服务器可以相互发送消息。
  3. 通信循环:客户端和服务器在建立的 WebSocket 连接上进行消息交换。
  4. 关闭握手:客户端或服务器发起关闭连接的请求,另一方响应,然后连接关闭。

因为以上优点这边将需要重新构建一套WebSocket工具类实现这边的要求:

工具类实现:

在 Spring 中封装 WebSocket 工具类通常涉及使用 Spring 提供的 WebSocket API。

WebSocketUtils

WebSocket 工具类封装示例,它使用 Spring 的 WebSocketSession 来发送消息给客户端。

  1. 异常处理: 在发送消息时,如果发生异常,我们可以添加更详细的异常处理逻辑。
  2. 会话管理: 我们可以添加同步块或使用 ConcurrentHashMap 的原子操作来确保线程安全。
  3. 用户标识符管理: 提供一个更灵活的方式来管理用户标识符和会话之间的关系。
  4. 事件发布: 使用 Spring 事件发布机制来解耦和管理 WebSocket 事件。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @Author derek_smart
 * @Date 202/5/11 10:05
 * @Description WebSocket 工具类
 */
@Component
public class WebSocketUtils extends TextWebSocketHandler {

    private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
    @Autowired
    private ApplicationEventPublisher eventPublisher;


    public void registerSession(String userIdentifier, WebSocketSession session) {
        sessions.put(userIdentifier, session);
        // Publish an event when a session is registered
        eventPublisher.publishEvent(new WebSocketSessionRegisteredEvent(this, session, userIdentifier));
    }

    public void removeSession(String userIdentifier) {
        WebSocketSession session = sessions.remove(userIdentifier);
        if (session != null) {
            // Publish an event when a session is removed
            eventPublisher.publishEvent(new WebSocketSessionRemovedEvent(this, session, userIdentifier));
        }
    }

    public void sendMessageToUser(String userIdentifier, String message) {
        WebSocketSession session = sessions.get(userIdentifier);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                // Handle the exception, e.g., logging or removing the session
                handleWebSocketException(session, e);
            }
        }
    }

    public void sendMessageToAllUsers(String message) {
        TextMessage textMessage = new TextMessage(message);
        sessions.forEach((userIdentifier, session) -> {
            if (session.isOpen()) {
                try {
                    session.sendMessage(textMessage);
                } catch (IOException e) {
                    // Handle the exception, e.g., logging or removing the session
                    handleWebSocketException(session, e);
                }
            }
        });
    }

    private void handleWebSocketException(WebSocketSession session, IOException e) {
        // Log the exception
        // Attempt to close the session if it's still open
        if (session.isOpen()) {
            try {
                session.close();
            } catch (IOException ex) {
                // Log the exception during close
            }
        }
        // Remove the session from the map
        sessions.values().remove(session);
        // Further exception handling...
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // This method can be overridden to handle connection established event
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // This method can be overridden to handle connection closed event
    }

    // Additional methods like handleTextMessage can be overridden if needed

    // Custom events
    public static class WebSocketSessionRegisteredEvent extends ApplicationEvent {
        private final WebSocketSession session;
        private final String userIdentifier;

        /**
         * Create a new WebSocketSessionRegisteredEvent.
         * @param source the object on which the event initially occurred (never {@code null})
         * @param session the WebSocket session which has been registered
         * @param userIdentifier the identifier of the user for whom the session is registered
         */
        public WebSocketSessionRegisteredEvent(Object source, WebSocketSession session, String userIdentifier) {
            super(source);
            this.session = session;
            this.userIdentifier = userIdentifier;
        }

        public WebSocketSession getSession() {
            return session;
        }

        public String getUserIdentifier() {
            return userIdentifier;
        }
    }

    public static class WebSocketSessionRemovedEvent extends ApplicationEvent {

        private final WebSocketSession session;
        private final String userIdentifier;

        /**
         * Create a new WebSocketSessionRemovedEvent.
         * @param source the object on which the event initially occurred (never {@code null})
         *      * @param session the WebSocket session which has been removed
         *      * @param userIdentifier the identifier of the user for whom the session was removed
         *      */
        public WebSocketSessionRemovedEvent(Object source, WebSocketSession session, String userIdentifier) {
            super(source);
            this.session = session;
            this.userIdentifier = userIdentifier;
        }

        public WebSocketSession getSession() {
            return session;
        }

        public String getUserIdentifier() {
            return userIdentifier;
        }
    }
}

 

 

在这个工具类中,我们使用了 ConcurrentHashMap 来存储和管理 WebSocket 会话。每个会话都与一个用户标识符相关联,这允许我们向特定用户发送消息。 使用了 ApplicationEventPublisher 来发布会话注册和移除事件,这样可以让其他组件在需要时响应这些事件。

另外,我们让 WebSocketUtils 继承了 TextWebSocketHandler,这样它可以直接作为一个 WebSocket 处理器。这意味着你可以重写
afterConnectionEstablished 和 afterConnectionClosed 方法来处理连接建立和关闭的事件,而不是在一个单独的 WebSocketHandler 中处理它们。

通过这些优化,WebSocketUtils 工具类变得更加健壮和灵活,能够更好地集成到 Spring 应用程序中

工具类提供了以下方法:

  • registerSession: 当新的 WebSocket 连接打开时,将该会话添加到映射中。
  • removeSession: 当 WebSocket 连接关闭时,从映射中移除该会话。
  • sendMessageToUser: 向特定用户发送文本消息。
  • sendMessageToAllUsers: 向所有连接的用户发送文本消息。
  • getSessions: 返回当前所有的 WebSocket 会话。

WebSocketHandler

为了完整地实现一个 WebSocket 工具类,你还需要创建一个 WebSocketHandler 来处理 WebSocket 事件,如下所示:

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
 * @Author derek_smart
 * @Date 202/5/11 10:05
 * @Description WebSocketHandler
 */
public class MyWebSocketHandler implements WebSocketHandler {

    private final WebSocketUtils webSocketUtils;

    public MyWebSocketHandler(WebSocketUtils webSocketUtils) {
        this.webSocketUtils = webSocketUtils;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userIdentifier = retrieveUserIdentifier(session);
        webSocketUtils.registerSession(userIdentifier, session);
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        // Handle incoming messages
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // Handle transport error
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String userIdentifier = retrieveUserIdentifier(session);
        webSocketUtils.removeSession(userIdentifier);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    private String retrieveUserIdentifier(WebSocketSession session) {
        // Implement logic to retrieve the user identifier from the session
        return session.getId(); // For example, use the WebSocket session ID
    }
}

在 MyWebSocketHandler 中,我们处理了 WebSocket 连接的建立和关闭事件,并且在这些事件发生时调用 WebSocketUtils 的方法注册或移除会话。

WebSocketConfig

要在 Spring 中配置 WebSocket,你需要在配置类中添加 WebSocketHandler 和 WebSocket 的映射。以下是一个简单的配置示例:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private final MyWebSocketHandler myWebSocketHandler;

    public WebSocketConfig(WebSocketUtils webSocketUtils) {
        this.myWebSocketHandler = new MyWebSocketHandler(webSocketUtils);
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myWebSocketHandler, "/ws").setAllowedOrigins("*");
    }
}

WebSocketEventListener

我们扩展了 ApplicationEvent 类,这是所有 Spring 事件的基类。我们添加了两个字段:session 和 userIdentifier,以及相应的构造函数和访问器方法。这样,当事件被发布时,监听器可以访问到事件的详细信息。

要发布这个事件,你需要注入 ApplicationEventPublisher 到你的 WebSocketUtils 类中,并在会话注册时调用 publishEvent 方法: 要发布这个事件,你需要在会话移除时调用 ApplicationEventPublisher 的 publishEvent 方法

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

@Component
public class WebSocketEventListener {

    @EventListener
    public void handleWebSocketSessionRegistered(WebSocketUtils.WebSocketSessionRegisteredEvent event) {
        // 处理会话注册事件
        WebSocketSession session = event.getSession();
        String userIdentifier = event.getUserIdentifier();

        // 你可以在这里添加你的逻辑,例如发送欢迎消息或记录会话信息
    }

    @EventListener
    public void handleWebSocketSessionRemoved(WebSocketUtils.WebSocketSessionRemovedEvent event) {
        // 处理会话移除事件
        WebSocketSession session = event.getSession();
        String userIdentifier = event.getUserIdentifier();

        // 你可以在这里添加你的逻辑,例如更新用户状态或释放资源
    }

}

类图:

 

在这个类图中,我们展示了以下类及其关系:

  • WebSocketUtils: 包含了会话管理和消息发送的方法。
  • WebSocketSessionRegisteredEvent: 一个自定义事件类,用于表示 WebSocket 会话注册事件。
  • WebSocketSessionRemovedEvent: 一个自定义事件类,用于表示 WebSocket 会话移除事件。
  • WebSocketEventListener: 一个监听器类,它监听并处理 WebSocket 会话相关的事件。
  • WebSocketController: 一个控制器类,用于处理 HTTP 请求并使用 WebSocketUtils 类的方法。
  • ChatWebSocketHandler: 一个 WebSocket 处理器类,用于处理 WebSocket 事件。
  • WebSocketConfig: 配置类,用于注册 WebSocket 处理器。

测试类实现:

WebSocketController

构建一个简单的聊天应用程序,我们将使用 WebSocketUtils 来管理 WebSocket 会话并向用户发送消息。 在这个示例中,我们将创建一个控制器来处理发送消息的请求,并使用 WebSocketUtils 类中的方法来实际发送消息。 首先,确保 WebSocketUtils 类已经被定义并包含了之前讨论的方法。 接下来,我们将创建一个 WebSocketController 来处理发送消息的请求:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
 * @Author derek_smart
 * @Date 202/5/11 10:09
 * @Description WebSocket 测试类
 */
@RestController
@RequestMapping("/chat")
public class WebSocketController {

    private final WebSocketUtils webSocketUtils;

    @Autowired
    public WebSocketController(WebSocketUtils webSocketUtils) {  
	this.webSocketUtils = webSocketUtils;
    }

    // 发送消息给特定用户
    @PostMapping("/send-to-user")
    public ResponseEntity<?> sendMessageToUser(@RequestParam String userIdentifier, @RequestParam String message) {
        try {
            webSocketUtils.sendMessageToUser(userIdentifier, message);
            return ResponseEntity.ok().build();
        } catch (IOException e) {
            // 日志记录异常,返回错误响应
            return ResponseEntity.status(500).body("Failed to send message to user.");
        }
    }

    // 发送广播消息给所有用户
    @PostMapping("/broadcast")
    public ResponseEntity<?> broadcastMessage(@RequestParam String message) {
        webSocketUtils.sendMessageToAllUsers(message);
        return ResponseEntity.ok().build();
    }
}

在 WebSocketController 中,我们提供了两个端点:一个用于向特定用户发送消息,另一个用于广播消息给所有连接的用户。

 

ChatWebSocketHandler

现在,让我们创建一个 WebSocketHandler 来处理 WebSocket 事件:

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class ChatWebSocketHandler extends TextWebSocketHandler {

    private final WebSocketUtils webSocketUtils;

    public ChatWebSocketHandler(WebSocketUtils webSocketUtils) {
        this.webSocketUtils = webSocketUtils;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 使用用户的唯一标识符注册会话
        String userIdentifier = retrieveUserIdentifier(session);
        webSocketUtils.registerSession(userIdentifier, session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 处理接收到的消息,例如在聊天室中广播
        String userIdentifier = retrieveUserIdentifier(session);
        webSocketUtils.sendMessageToAllUsers("User " + userIdentifier + " says: " + message.getPayload());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 移除会话
        String userIdentifier = retrieveUserIdentifier(session);
        webSocketUtils.removeSession(userIdentifier);
    }

    private String retrieveUserIdentifier(WebSocketSession session) {
        // 根据实际情况提取用户标识符,这里假设使用 WebSocketSession 的 ID
        return session.getId();
    }
}

在 ChatWebSocketHandler 中,我们处理了连接建立和关闭事件,并在这些事件发生时调用 WebSocketUtils 的方法注册或移除会话。我们还实现了 handleTextMessage 方法来处理接收到的文本消息。

WebSocketConfig

最后,我们需要配置 WebSocket 端点:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private final ChatWebSocketHandler chatWebSocketHandler;

    public WebSocketConfig(WebSocketUtils webSocketUtils) {
        this.chatWebSocketHandler = new ChatWebSocketHandler(webSocketUtils);
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(chatWebSocketHandler, "/ws/chat").setAllowedOrigins("*");
    }
}

在 WebSocketConfig 配置类中,我们注册了 ChatWebSocketHandler 到 /ws/chat 路径。这样,客户端就可以通过这个路径来建立 WebSocket 连接。

这个示例展示了如何使用 WebSocketUtils 工具类来管理 WebSocket 会话,并通过 REST 控制器端点发送消息。客户端可以连接到 WebSocket 端点,并使用提供的 REST 端点发送和接收消息。

总结:

总的来说,WebSocket 在需要快速、实时、双向通信的应用中提供了显著的优势,例如在线游戏、聊天应用、实时数据监控和协作工具。然而,不是所有的场景都需要 WebSocket 的能力,对于不需要实时通信的应用,传统的 HTTP 请求或 HTTP 轮询可能仍然是合适的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1681686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

制氧机负离子的作用与好处深度解析

随着现代生活节奏的加快&#xff0c;空气质量逐渐下降&#xff0c;人们对健康生活的追求也日益增强。在这样的背景下&#xff0c;制氧机负离子功能因其多重健康效益而备受关注。本文将深入探讨制氧机负离子的作用与功效&#xff0c;帮助大家更好地了解并应用这一健康科技。 我们…

Win11环境下,AirSim和UE4安装

这里主要描述一下我前段时间成功安装AirSim和UE4的流程&#xff0c;中间也遇到过一些问题&#xff0c;刚开始我安装的是最新的UE5&#xff0c;但是不知道什么情况运行AirSim中的blocks模块始终没有成功&#xff0c;VisualStudio中提示Unable to find plugin PhysXVehicles &…

回收站删除的文件怎么恢复?6个恢复技巧记得收藏!

“回收站删除的文件还有机会恢复吗&#xff1f;应该怎么操作才能恢复回收站里删除的文件呀&#xff1f;本人纯小白&#xff0c;希望大家推荐几个简单易懂的恢复方法。” 在使用电脑的过程中&#xff0c;我们时常会不小心将重要文件误删到回收站&#xff0c;甚至直接从回收站中彻…

AI预测体彩排3采取878定位大底=23策略+杀断组+杀组选+杀和尾+杀和值012缩水测试5月16日预测第2弹

昨天的87823大底测试第一次测试&#xff0c;已经成功命中! 今天继续测试&#xff0c;仍旧目标为&#xff1a;10期中至少5中期。好了&#xff0c;废话不多说了&#xff0c;直接上结果吧~ 首先&#xff0c;878定位如下&#xff1a; 百位&#xff1a;4,5,6,3,8,1,9,0…

控制台的高度可调有哪些重要意义解析

在现代办公环境中&#xff0c;控制台的高度可调性越来越受到重视。它不仅为员工提供了更加舒适的工作环境&#xff0c;还提高了工作效率和生产力。本文将详细探讨控制台高度可调的重要性&#xff0c;并解析其在实际应用中的优势。 个性化适应需求 对于长时间在控制台前工作的用…

Kubernetes入门:应用部署方式的演变

Kubernetes&#xff1a;管理云平台中 多主机的 容器化应用的平台。 应用部署方式的演变 传统部署 互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;无法为物理服务器中的应用程序定义资源边…

用AI帮你写简历,入职啦简历编辑器

简历的重要性 在当前就业形势严峻、竞争加剧的背景下&#xff0c;获取理想工作的难度与日俱增。此时&#xff0c;一份精心准备、亮点突出的简历&#xff0c;成为了您脱颖而出、成功获得面试机会乃至工作offer的关键。面对HR有限的审阅时间和众多应聘者的激烈角逐&#xff0c;如…

Flink LookupJoin攒批查询

Flink LookupJoin攒批查询 需求背景 使用Lookup Join进行维表关联时&#xff0c;流表数据需要实时与维表数据进行关联。使用Cache会导致数据关联不准确&#xff0c;不使用Cache会造成数据库服务压力。攒批查询是指攒够一定批数量的数据&#xff0c;相同的查询Key只查询一次&a…

苹果永久版安装PD虚拟机:Parallels Desktop 19 一键激活版

Parallels Desktop 19是一款功能强大的虚拟机软件&#xff0c;专为Mac用户设计&#xff0c;允许用户在同一台Mac电脑上同时运行Windows、Linux等多个操作系统&#xff0c;而无需额外的硬件设备。 下载地址&#xff1a;https://www.macz.com/mac/9581.html?idOTI2NjQ5Jl8mMjcuM…

力扣HOT100 - 32. 最长有效括号

解题思路&#xff1a; 栈 class Solution {public int longestValidParentheses(String s) {int max 0;// 也可以使用 Stack<Integer> stacknew Stack<>();但Stack是遗留类&#xff0c;不推荐Deque<Integer> stack new LinkedList<>();stack.push(…

Nginx HTTPS配置:一篇文章解决所有痛点

你好呀&#xff0c;我是赵兴晨&#xff0c;文科程序员。 今天&#xff0c;我将与大家分享一些关于Nginx的实用知识。这次的主题是&#xff1a;如何为Nginx配置HTTPS。我将从HTTP与HTTPS的区别讲起&#xff0c;然后逐步介绍Nginx的安装与HTTPS配置的详细步骤。 HTTP与HTTPS的区…

Github项目部署到自己的域名

天坑&#xff0c;买的是华为的域名&#xff0c;不过新用户才一块&#xff0c;就忍了 要买个域名&#xff0c;我买的是华为的&#xff08;此处是购买地址&#xff09; 购买后去控制台&#xff0c;点击“总览”进入域名页面 点击想要修改的域名后的“管理解析” 点击快速解析&…

vant添加列表, 日期选择总是填充到最后一个组内原因

添加多个行程, 无论在哪个行程上修改时间, 时间总是只显示在最后一个行程里 错误代码: <div class"journey"><divv-for"(item, index) in ruleform.hrms_business_item":key"index"><div class"journey-title">&l…

人工智能创新领衔,Android系统如虎添翼:2024 Google I/O 大会深度解析

人工智能创新领衔&#xff0c;Android系统如虎添翼&#xff1a;2024 Google I/O 大会深度解析 2024年5月14日举行的Google I/O大会&#xff0c;犹如一场精彩的科技盛宴&#xff0c;吸引了全球的目光。大会上&#xff0c;谷歌发布了一系列重磅产品和技术更新&#xff0c;展现了…

TMC4671超越传感器和摄像头之外——将物联网从云端转移到现实世界[应用案例]

在早期&#xff0c;物联网 (IoT) 在很大程度上充当了云端服务的“眼睛和耳朵”&#xff0c;收集传感器、摄像头和其他物理世界输入设备的数据&#xff0c;而不太注重操纵或控制它所监视的事物。支持物联网的自动化和机器人应用已经开始合并&#xff0c;但是一般而言&#xff0c…

SIP-7041 20W SIP广播网络有源音箱 校园广播20W木质SIP音箱

SIP-7041 20W SIP广播网络有源音箱 校园广播20W木质SIP音箱 一、描述 SIP-7041 20W SIP广播网络有源音箱 支持标准SIP协议 SIP-7041是我司的一款壁挂式网络有源音箱&#xff0c;具有10/100M以太网接口&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;可达到…

win10专业版或者企业版安装docker,亲测过,请放心安装

in10专业版系统首先需要开启硬件虚拟化及Hyper-V功能&#xff0c;才能进行Docker for Windows软件安装 1.鼠标在桌面最下面&#xff0c;右击–任务管理器—性能—虚拟化&#xff08;已启用&#xff09;&#xff0c;看到这个已启用就说明OK啦&#xff1b; 2.win10系统&#xff…

iPhone携手ChatGPT?苹果OpenAI或将强强联手

近年来&#xff0c;人工智能技术的蓬勃发展掀起了一场席卷全球的科技浪潮&#xff0c;而智能手机作为人们日常生活中不可或缺的一部分&#xff0c;自然成为了这场AI竞赛的重要战场。各大科技巨头纷纷加码布局&#xff0c;力图在AI领域占据领先地位。 近日&#xff0c;据知情人士…

STAR-Echo:一种使用时空分析和基于Transformer的影像组学模型预后慢性肾脏病患者 MACE 预后的新型生物标志物

文章目录 STAR-Echo: A Novel Biomarker for Prognosis of MACE in Chronic Kidney Disease Patients Using Spatiotemporal Analysis and Transformer-Based Radiomics Models摘要方法实验结果 STAR-Echo: A Novel Biomarker for Prognosis of MACE in Chronic Kidney Disease…

LLMjacking:针对云托管AI大模型服务的新型攻击

Sysdig威胁研究团队(TRT)观察到一种新型攻击&#xff0c;命名为LLMjacking。它利用窃取的云凭证&#xff0c;对托管在云上的十个大型语言模型(LLM)服务发起攻击。 这些凭证是从一个流行的目标获得&#xff0c;即运行着一个存在漏洞的Laravel版本&#xff08;CVE-2021-3129&…