SpringBoot 2.7 集成 Netty 4 模拟服务端与客户端通讯入门教程

文章目录

    • 1 摘要
    • 2 核心 Maven 依赖
    • 3 核心代码
      • 3.1 服务端事务处理器 (DemoNettyServerHandler)
      • 3.2 服务端连接类(InitNettyServer)
      • 3.3 客户端事务处理器(DemoNettyClientHandler)
      • 3.4 客户端连接类(DemoNettyClient)
    • 4 测试
      • 4.1 测试流程
      • 4.2 测试结果
      • 4.3 测试结论
    • 5 推荐参考资料
    • 6 Github 源码

1 摘要

Netty 作为一款 NIO 底层通讯框架,在高并发场景有着广泛的应用,众多消息中间件内部也是基于 Netty 进行开发。本文将介绍基于 SpringBoot 2.7 集成 Netty 来模拟服务端与客户端进行通讯。

2 核心 Maven 依赖

demo-netty-server/pom.xml
        <!-- Netty --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>${netty.version}</version></dependency>

netty 版本:

<netty.version>4.1.96.Final</netty.version>

3 核心代码

3.1 服务端事务处理器 (DemoNettyServerHandler)

demo-netty-server/src/main/java/com/ljq/demo/springboot/netty/server/handler/DemoNettyServerHandler.java
package com.ljq.demo.springboot.netty.server.handler;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Description: Netty 服务处理器* @Author: junqiang.lu* @Date: 2023/8/18*/
@Slf4j
@Component
// 标记该类实例可以被多个 channel 共享
@ChannelHandler.Sharable
public class DemoNettyServerHandler extends ChannelInboundHandlerAdapter {/*** 每个传入的消息都会调用该方法** @param ctx* @param msg*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ByteBuf byteBuf = (ByteBuf) msg;byte[] body = new byte[byteBuf.readableBytes()];byteBuf.readBytes(body);log.info("服务端接收到数据:{}", new String(body));byte[] responseBytes = "hi,客户端,我是服务端".getBytes();ctx.writeAndFlush(Unpooled.wrappedBuffer(responseBytes));}/*** 在读取期间,有异常抛出时会调用** @param ctx* @param cause*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// 打印异常栈跟踪log.error("netty server error",cause);//关闭该channelctx.close();}}

3.2 服务端连接类(InitNettyServer)

demo-netty-server/src/main/java/com/ljq/demo/springboot/netty/server/init/InitNettyServer.java
package com.ljq.demo.springboot.netty.server.init;import com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.net.InetSocketAddress;/*** @Description: 初始化 netty 服务端* @Author: junqiang.lu* @Date: 2023/8/18*/
@Slf4j
@Component
public class InitNettyServer implements ApplicationRunner {@Value("${netty.port:9120}")private Integer nettyPort;@Resourceprivate DemoNettyServerHandler nettyServerHandler;@Overridepublic void run(ApplicationArguments args) throws Exception {this.start();}/*** 启动服务** @throws InterruptedException*/public void start() throws InterruptedException {// 连接管理线程池EventLoopGroup mainGroup = new NioEventLoopGroup(1);// 工作线程池EventLoopGroup workGroup = new NioEventLoopGroup(4);ServerBootstrap bootstrap = new ServerBootstrap();try {bootstrap.group(mainGroup, workGroup)// 指定 nio 通道.channel(NioServerSocketChannel.class)// 指定 socket 地址和端口.localAddress(new InetSocketAddress(nettyPort))// 添加子通道 handler.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(nettyServerHandler);}});// 异步绑定服务器,调用sync()方法阻塞等待直到绑定完成ChannelFuture channelFuture = bootstrap.bind().sync();log.info("---------- [init] netty server start ----------");// 获取Channel的CloseFuture,并且阻塞当前线程直到它完成channelFuture.channel().closeFuture().sync();} finally {// 关闭 EventLoopGroup,释放资源mainGroup.shutdownGracefully().sync();}}}

注意事项: 为什么要定义两个 EventLoopGroup ? 实际上这个 EventLoopGroup 相当于一个线程组,如果只定义一个,则这个线程组既负责服务端与客户端的连接管理,也负责服务端的事务处理,这就大大降低了服务端的吞吐量,定义两个 EventLoopGroup 可以极大地提高系统的并发性能。

3.3 客户端事务处理器(DemoNettyClientHandler)

demo-netty-server/src/main/java/com/ljq/demo/springboot/netty/server/handler/DemoNettyClientHandler.java
package com.ljq.demo.springboot.netty.server.handler;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;/*** @Description: netty 客户端处理器* @Author: junqiang.lu* @Date: 2023/8/18*/
@Slf4j
@Component
// 标记该类实例可以被多个 channel 共享
@ChannelHandler.Sharable
public class DemoNettyClientHandler extends ChannelInboundHandlerAdapter {/*** 接收消息** @param ctx* @param msg* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf byteBuf= (ByteBuf)msg;byte[] body=new byte[byteBuf.readableBytes()];byteBuf.readBytes(body);log.info("接收到来自服务端的消息:{}",new String(body));// 释放消息ReferenceCountUtil.release(msg);}/*** 和服务器建立连接时触发** @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {log.info("客户端连接到服务器!!!");// 向服务端发送上线消息byte[] bytes = "hi,服务端,我是客户端!".getBytes();ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);ctx.writeAndFlush(byteBuf);}/*** 有异常时触发** @param ctx* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {log.error("客户端异常", cause);super.exceptionCaught(ctx, cause);}
}

3.4 客户端连接类(DemoNettyClient)

demo-netty-server/src/main/java/com/ljq/demo/springboot/netty/server/client/DemoNettyClient.java
package com.ljq.demo.springboot.netty.server.client;import com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;/*** @Description: Netty 客户端* @Author: junqiang.lu* @Date: 2023/8/22*/
@Slf4j
public class DemoNettyClient {private final String host;private final int port;private final EventLoopGroup mainGroup;private final Bootstrap bootstrap;private Channel channel;public DemoNettyClient(String host, int port) {this.host = host;this.port = port;this.mainGroup = new NioEventLoopGroup();this.bootstrap = new Bootstrap();}public Channel getChannel() {return this.channel;}/*** 创建连接*/public void connect() throws InterruptedException {bootstrap.group(mainGroup).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port)).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new StringEncoder()).addLast(new ByteArrayEncoder()).addLast(new DemoNettyClientHandler());}});ChannelFuture future = bootstrap.connect().sync();this.channel = future.channel();}/*** 发送消息** @param message*/public void sendMessage(String message) {log.info("客户端待发送消息:{}", message);Channel channel = this.getChannel();byte[] bytes = message.getBytes(StandardCharsets.UTF_8);ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);channel.writeAndFlush(byteBuf);}public void close() throws InterruptedException {log.info("关闭客户端");mainGroup.shutdownGracefully().sync();}public static void main(String[] args) throws InterruptedException {String serverHost = "127.0.0.1";int serverPort = 9120;String message = "ahahaha啊哈哈哈啊哈";DemoNettyClient nettyClient = new DemoNettyClient(serverHost, serverPort);nettyClient.connect();for (int i = 0; i < 10; i++) {nettyClient.sendMessage(message + i);}log.info("--------开始休眠 5 秒------------");Thread.sleep(5000L);for (int i = 0; i < 5; i++) {nettyClient.sendMessage(i + message);Thread.sleep(100L);}nettyClient.close();}}

4 测试

4.1 测试流程

先启动 SpringBoot 应用程序,此时服务端已经启动完成

再执行客户端连接类(DemoNettyClient) 中的 main 方法

4.2 测试结果

客户端日志:

2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈0
2023-08-23 11:27:11 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 48| 客户端连接到服务器!!!
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈1
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈2
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈3
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈4
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈5
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈6
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈7
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈8
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:ahahaha啊哈哈哈啊哈9
2023-08-23 11:27:11 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 96| --------开始休眠 5 秒------------
2023-08-23 11:27:11 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:16 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:0ahahaha啊哈哈哈啊哈
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:16 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:1ahahaha啊哈哈哈啊哈
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:16 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:2ahahaha啊哈哈哈啊哈
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:17 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:3ahahaha啊哈哈哈啊哈
2023-08-23 11:27:17 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:17 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 74| 客户端待发送消息:4ahahaha啊哈哈哈啊哈
2023-08-23 11:27:17 | INFO  | nioEventLoopGroup-2-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyClientHandler 35| 接收到来自服务端的消息:hi,客户端,我是服务端
2023-08-23 11:27:17 | INFO  | main | com.ljq.demo.springboot.netty.server.client.DemoNettyClient 82| 关闭客户端
Disconnected from the target VM, address: '127.0.0.1:56274', transport: 'socket'

服务端日志:

2023-08-23 11:27:11 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:hi,服务端,我是客户端!ahahaha啊哈哈哈啊哈0ahahaha啊哈哈哈啊哈1ahahaha啊哈哈哈啊哈2ahahaha啊哈哈哈啊哈3ahahaha啊哈哈哈啊哈4ahahaha啊哈哈哈啊哈5ahahaha啊哈哈哈啊哈6ahahaha啊哈哈哈啊哈7ahahaha啊哈哈哈啊哈8ahahaha啊哈哈哈啊哈9
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:0ahahaha啊哈哈哈啊哈
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:1ahahaha啊哈哈哈啊哈
2023-08-23 11:27:16 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:2ahahaha啊哈哈哈啊哈
2023-08-23 11:27:17 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:3ahahaha啊哈哈哈啊哈
2023-08-23 11:27:17 | INFO  | nioEventLoopGroup-3-1 | com.ljq.demo.springboot.netty.server.handler.DemoNettyServerHandler 33| 服务端接收到数据:4ahahaha啊哈哈哈啊哈

4.3 测试结论

  • (1)从日志中可以看出客户端与服务端已经通讯成功
  • (2)在第一个客户端循环发送消息中,客户端发了10次,然而服务端一次性接收到了所有消息,并不是发送一次接收一次
  • (3)在第二个客户端循环发送消息过程中,客户端每间隔100毫秒发送一条消息,此时服务端是一条一条接收的

关于第(2)点,这是 Netty 通讯过程中的粘包问题,欲知如何解决,且听下回分解。

关于第(3)点,由于添加了时间间隔,Netty 会认为一条消息发送完成,因此就能发送一条接收一条。

5 推荐参考资料

[Netty入门] 最简单的Netty应用程序实例

Netty4:一个简单的消息传递的demo(分析和解析)

6 Github 源码

Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo/tree/master/demo-netty-server

个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
404Code

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

通过安全日志读取WFP防火墙放行日志

前言 之前的文档中&#xff0c;描写了如何对WFP防火墙进行操作以及如何在防火墙日志中读取被防火墙拦截网络通讯的日志。这边文档&#xff0c;着重描述如何读取操作系统中所有被放行的网络通信行为。 读取系统中放行的网络通信行为日志&#xff0c;在win10之后的操作系统上&am…

继承(C++)

继承 一、初识继承概念“登场”语法格式 继承方式九种继承方式组合小结&#xff08;对九种组合解释&#xff09; 二、继承的特性赋值转换 一一 切片 / 切割作用域 一一 隐藏 / 重定义 三、派生类的默认成员函数派生类的默认成员函数1. 构造函数2. 拷贝构造3. 赋值运算符重载4. …

【编织时空三:探究顺序表与链表的数据之旅】

本章重点 链表OJ题 1. 删除链表中等于给定值 val 的所有结点。 OJ链接 思路一&#xff1a;删除头结点时另做考虑&#xff08;由于头结点没有前一个结点&#xff09; struct ListNode* removeElements(struct ListNode* head, int val) {assert(head);struct ListNode* cur h…

Go:测试框架GoConvey 简介

快速开始 GoConvey是一个完全兼容官方Go Test的测试框架&#xff0c;一般来说这种第三方库都比官方的功能要强大、更加易于使用、开发效率更高&#xff0c;闲话少说&#xff0c;先看一个example&#xff1a; package utils import (. "github.com/smartystreets/goconvey…

【JVM】运行时数据区域

文章目录 说明程序计数器虚拟机栈本地方法栈Java堆方法区运行时常量池直接内存 说明 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途&#xff0c;以及创建和销毁的时间&#xff0c;有的区域随着虚拟机进程的启动而一直…

【广州华锐互动】牲畜养殖VR模拟实操系统为传统教育注入新的生命力

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐走进我们的生活。在农业领域&#xff0c;VR技术的应用也日益广泛&#xff0c;为现代农业人才培养提供了新的途径。 由广州华锐互动开发的“牲畜养殖VR模拟实操系统”引起了广泛关注&#xff0c;系统包含了鸡、猪、牛、马…

产品流程图是什么?怎么做?

产品流程图是什么&#xff1f; 产品流程图是一种图形化的表达方式&#xff0c;用于描述产品开发、制造、销售、使用等各个阶段中涉及的流程、步骤和关系。它通过图形符号、箭头、文本等元素&#xff0c;展示了产品的各个环节之间的关联和顺序&#xff0c;通常被用于可视化产…

STM32 F103C8T6学习笔记12:红外遥控—红外解码-位带操作

今日学习一下红外遥控的解码使用&#xff0c;红外遥控在日常生活必不可少&#xff0c;它的解码与使用也是学习单片机的一个小过程&#xff0c;我们将通过实践来实现它。 文章提供源码、测试工程下载、测试效果图。 目录 红外遥控原理&#xff1a; 红外遥控特点&#xff1a; …

Qt+C++串口调试接收发送数据曲线图

程序示例精选 QtC串口调试接收发送数据曲线图 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<QtC串口调试接收发送数据曲线图>>编写代码&#xff0c;代码整洁&#xff0c;规则&…

探索GreatADM:图形化部署MGR的全新体验

摘要&#xff1a; 在DBA的日常工作中&#xff0c;快速部署数据库高可用架构&#xff0c;且标准化地入网部署数据库是一项重要的基础任务。本文将介绍常见的部署MGR的方式&#xff0c;并重点介绍万里数据库的GreatADM数据库管理平台进行图形化、可视化、标准化的部署过程&#x…

vue 学习笔记 简单实验

1.代码(html) <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"counter">Counter: {{ counter }} </div> <script> const Counter {data() {return {counter: 5}} } Vue.cr…

二、pikachu之SQL注入(2)

文章目录 1、delete注入2、http header注入3、布尔盲注4、时间盲注 4、宽字节注入 1、delete注入 &#xff08;1&#xff09;寻找传参页面&#xff0c;在删除留言的时候&#xff0c;发现是get传参&#xff1b; &#xff08;2&#xff09;判断是否存在注入点&#xff0c;命令&…

Shell语法揭秘:深入探讨常见Linux Shell之间的语法转换

深入探讨常见Linux Shell之间的语法转换 一、引言二、Linux常用Shell&#xff1a;Bash、Zsh、Ksh、Csh、Tcsh和Fish的简介2.1、Bash、Zsh、Ksh、Csh、Tcsh和Fish的特点和用途2.2、语法差异是常见Shell之间的主要区别 三、变量和环境设置的语法差异3.1、变量定义和使用的不同语法…

Redis——set类型详解

概要 Set&#xff08;集合&#xff09;&#xff0c;将一些有关联的数据放到一起&#xff0c;集合中的元素是无序的&#xff0c;并且集合中的元素是不能重复的 之前介绍的list就是有序的&#xff0c;对于列表来说[1, 2, 3] 和 [2, 1, 3]是两个不同的列表&#xff0c;而对于集合…

GraphScope,开源图数据分析引擎的领航者

文章首发地址 GraphScope是一个开源的大规模图数据分析引擎&#xff0c;由Aliyun、阿里巴巴集团和华为公司共同开发。GraphScope旨在为大规模图数据处理和分析提供高性能、高效率的解决方案。 Github地址&#xff1a; https://github.com/alibaba/GraphScope GraphScope 的重…

开发第一个gPRC的开发

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

网络综合布线实训室方案(2023版)

综合布线实训室概述 随着智慧城市的蓬勃发展,人工智能、物联网、云计算、大数据等新兴行业也随之崛起,网络布线系统作为现代智慧城市、智慧社区、智能建筑、智能家居、智能工厂和现代服务业的基础设施和神经网络,发挥着重要作用。实践表明,网络系统故障的70%发生在布线系统,直接…

苹果手机桌面APP带云图标有个箭头,过一段时间经常要下载才能使用APP

环境&#xff1a; IPhone 11 IOS13.0 问题描述&#xff1a; 苹果手机桌面APP带云图标有个箭头&#xff0c;过一段时间经常要下载才能使用APP 解决方案&#xff1a; 1.打开设置&#xff0c;往下找到iTunes Store与App Store 2.找到下面卸载未使用的APP 关闭按钮

最优的家电设备交互方式是什么?详解家电设备交互的演进之旅

家电&#xff0c;在人们的日常生活中扮演着不可或缺的角色&#xff0c;也是提升人们幸福感的重要组成部分&#xff0c;那你了解家电的发展史吗&#xff1f; 70年代 结婚流行“四大件”&#xff1a;手表、自行车、缝纫机&#xff0c;收音机&#xff0c;合成“三转一响”。 80年…

问题描述:在Windows下没有预装ImageMagick工具

问题描述:在Windows下没有预装ImageMagick工具 # WInR输入cmd回车进入命令行,执行以下命令查看版本信息 magick --version没有预装ImageMagick工具 解决方案&#xff1a;下载安装ImageMagick 官网下载:ImageMagick-7.1.1-15-Q16-x64-dll.exe 下载之后&#xff0c;一路下一步…