当前位置: 首页 > news >正文

Netty笔记03-组件Channel

文章目录

  • Channel概述
    • Channel 的概念
    • Channel 的主要功能
    • Channel 的生命周期
    • Channel 的状态
    • Channel 的类型
    • channel 的主要方法
  • ChannelFuture
  • CloseFuture
    • 💡 netty异步提升的是什么
    • 要点
    • 总结


Channel概述

Channel 的概念

在 Netty 中,Channel 是一个非常重要的概念,它代表了网络连接的抽象(一个网络连接),用于进行数据的读取和写入操作。Channel 是 Netty 中处理网络 I/O 的核心组件之一,它将网络 I/O 操作封装在一个统一的接口下,使得开发人员可以更容易地编写高性能的网络应用程序。

Channel 是 Netty 中的接口,它定义了一组基本的操作,如打开、关闭、读取、写入等。Netty 为不同的网络通信协议提供了不同的Channel 实现,比如 TCP 的 SocketChannel,UDP 的 DatagramChannel 等。

Channel 的主要功能

  • 读取数据:从网络中读取数据。
  • 写入数据:将数据写入到网络中。
  • 关闭连接:关闭当前的 Channel。
  • 绑定地址:将 Channel 绑定到特定的本地地址(IP 地址和端口号)。
  • 注册到 EventLoop:将 Channel 注册到 EventLoop,以便处理 I/O 事件。
  • 获取ChannelPipeline:每个 Channel 都有一个 ChannelPipeline,用于处理入站和出站的数据流。

Channel 的生命周期

一个 Channel 的生命周期通常包括以下几个阶段:

  • 创建:当一个新连接建立时,Netty 会创建一个新的 Channel。
  • 注册:新创建的 Channel 会注册到一个 EventLoop 上。
  • 激活:当 Channel 被绑定到一个 Socket 地址时,它会变为激活状态。
  • 读写操作:Channel 可以进行读取和写入操作。
  • 关闭:当连接关闭时,Channel 也会被关闭。

Channel 的状态

Channel 有几个重要的状态,包括但不限于:

  • 注册状态:Channel 是否已经被注册到 EventLoop。
channel.isRegistered() // 检查是否已注册
  • 活跃状态:Channel 是否已经绑定到一个 Socket 地址。
channel.isActive() // 检查是否活跃
  • 打开状态:Channel 是否处于打开状态。
channel.isOpen() // 检查是否打开

Channel 的类型

Netty 为不同的网络通信协议提供了不同的 Channel 实现:

  • TCP 通信:NioServerSocketChannel 和 NioSocketChannel。
  • UDP 通信:NioDatagramChannel。
  • 文件传输:FileRegion。

channel 的主要方法

  • close() 可以用来关闭 channel
  • closeFuture() 用来处理 channel 的关闭
    • sync 方法作用是同步等待 channel 关闭
    • 而 addListener 方法是异步等待 channel 关闭
  • pipeline() 方法添加处理器
  • write() 方法将数据写入(只会将数据写入到channel的缓冲区中,具体什么时候发送数据则有很多条件,如在执行flush()或缓冲区达到一定大小)
  • writeAndFlush() 方法将数据写入并刷出(将数据写入到channel的缓冲区中并立刻从缓冲区中发出)

ChannelFuture

使用上一章的服务器端和客户端代码
服务器端

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import jdk.internal.org.objectweb.asm.Handle;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.Charset;@Slf4j
public class EventLoopServer {public static void main(String[] args) {// 创建一个独立的 EventLoopGroup,将耗时的代码放到一个额外的组中线程处理EventLoopGroup group = new DefaultEventLoopGroup();new ServerBootstrap()// boss 和 worker//  boss 只负责 ServerSocketChannel 上 accept 事件//  worker 只负责 socketChannel 上的读写//group参数1:boss不需要指定线程数,因为ServerSocketChannel只会跟一个EventLoop进行绑定,//              又因为服务器只有一个,所以只会占用一个线程,不用指定线程数。//group参数1:work线程指定为两个.group(new NioEventLoopGroup(), new NioEventLoopGroup(2)).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel ch) throws Exception {//如果读操作耗费的时间很上,会影响其他客户端的读写操作,// 一个work管理多个channel,如果其中一个耗时过长则会影响其他channel的读写操作ch.pipeline().addLast("handler1", new ChannelInboundHandlerAdapter() {/*** @param ctx* @param msg ByteBuf类型* @throws Exception*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;log.debug(buf.toString(Charset.defaultCharset()));ctx.fireChannelRead(msg); // 让消息传递给下一个handler,如果不添加则消息不会传递给handler2中}}).addLast(group, "handler2", new ChannelInboundHandlerAdapter() {@Override                                         // ByteBufpublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;log.debug(buf.toString(Charset.defaultCharset()));}});}}).bind(8080);}
}

客户端代码

new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new StringEncoder());}}).connect("127.0.0.1", 8080).sync().channel().writeAndFlush(new Date() + ": hello world!");

拆分上面的代码

ChannelFuture channelFuture = new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {@Overrideprotected void initChannel(Channel ch) {ch.pipeline().addLast(new StringEncoder());}}).connect("127.0.0.1", 8080); // 标记1channelFuture.sync()
.channel()
.writeAndFlush(new Date() + ": hello world!");
  • 1 处返回的是 ChannelFuture 对象,它的作用是利用 channel() 方法来获取 Channel 对象

注意 connect 方法是异步的,意味着不等连接建立,方法执行就返回了。因此 channelFuture 对象中不能【立刻】获得到正确的 Channel 对象。

实验如下:

// 2. 类中带有 Future,Promise 的类型都是和异步方法配套使用,用来处理结果
ChannelFuture channelFuture = new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Override // 在连接建立后被调用protected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new StringEncoder());}})// 1. 连接到服务器// 异步非阻塞, main 发起了调用,真正执行 connect 是 nio 线程.connect(new InetSocketAddress("localhost", 8080)); // 1s 秒后// 2.1 使用 sync 方法同步处理结果
//1.
System.out.println("sync前:"+channelFuture.channel());//[id: 0xe3489549]
//2.
channelFuture.sync(); // 阻塞住当前线程,直到nio线程连接建立完毕
//3.
System.out.println("sync后:"+channelFuture.channel());//[id: 0xe3489549, L:/127.0.0.1:54398 - R:localhost/127.0.0.1:8080]
Channel channel = channelFuture.channel();
log.debug("{}", channel);
//[DEBUG] [main] c.i.n.c.EventLoopClient - [id: 0xf540a105, L:/127.0.0.1:20434 - R:localhost/127.0.0.1:8080]
channel.writeAndFlush("hello, world");
channel.writeAndFlush("hello, world");
channel.writeAndFlush("hello, world");
System.out.println();
  • 执行到 1 时,连接未建立,打印 [id: 0xe3489549]
  • 执行到 2 时,sync 方法是同步等待连接建立完成
  • 执行到 3 时,连接肯定建立了,打印 [id: 0xe3489549, L:/127.0.0.1:54398 - R:localhost/127.0.0.1:8080]

除了用 sync 方法可以让异步操作同步以外,还可以使用回调的方式:

// 2. 类中带有 Future,Promise 的类型都是和异步方法配套使用,用来处理结果ChannelFuture channelFuture = new Bootstrap().group(new NioEventLoopGroup()).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Override // 在连接建立后被调用protected void initChannel(NioSocketChannel ch) throws Exception {ch.pipeline().addLast(new StringEncoder());}})// 1. 连接到服务器// 异步非阻塞, main 发起了调用,真正执行 connect 是 nio 线程.connect(new InetSocketAddress("localhost", 8080)); // 1s 秒后// 2.2 使用 addListener(回调对象) 方法异步(将main线程处理的活交给其他线程)处理结果//  将等待连接建立、连接成功后处理结果全部交给其他的线程处理//1.System.out.println("sync前:"+channelFuture.channel());//[id: 0x3baac75f]channelFuture.addListener(new ChannelFutureListener() {
/**         ↑*          ↑     ←      ←       ↑*                               ↑* @param future 该future对象就是channelFuture对象* @throws Exception*/@Override// 在 nio 线程连接建立好之后,会调用 operationCompletepublic void operationComplete(ChannelFuture future) throws Exception {//2.System.out.println("sync后:"+channelFuture.channel());//[id: 0x3baac75f, L:/127.0.0.1:54744 - R:localhost/127.0.0.1:8080]Channel channel = future.channel();log.debug("{}", channel);//[DEBUG] [nioEventLoopGroup-2-1] c.i.n.c.EventLoopClient - [id: 0x2ac4448f, L:/127.0.0.1:20346 - R:localhost/127.0.0.1:8080]channel.writeAndFlush("hello, world");//调用以上三行代码的还是nio线程}});
  • 执行到 1 时,连接未建立,打印 [id: 0x3baac75f]
  • ChannelFutureListener 会在连接建立时被调用(其中 operationComplete 方法),因此执行到 2 时,连接肯定建立了,打印 [id: 0x3baac75f, L:/127.0.0.1:54744 - R:localhost/127.0.0.1:8080]

CloseFuture

需求:客户端的控制台不断的接受用户的输入,将用户输入的信息发给服务器端, 当不想发送信息时,输入q退出。

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.Scanner;@Slf4j
public class CloseFutureClient {public static void main(String[] args) throws InterruptedException {NioEventLoopGroup group = new NioEventLoopGroup();ChannelFuture channelFuture = new Bootstrap().group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<NioSocketChannel>() {@Override // 在连接建立后被调用protected void initChannel(NioSocketChannel ch) throws Exception {//LoggingHandler一般用于调试使用,会将channel运行流程、状态显示出来ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));ch.pipeline().addLast(new StringEncoder());}}).connect(new InetSocketAddress("localhost", 8080));System.out.println(channelFuture.getClass());Channel channel = channelFuture.sync().channel();log.debug("{}", channel);new Thread(()->{Scanner scanner = new Scanner(System.in);//不断拿到用户输入的信息while (true) {String line = scanner.nextLine();if ("q".equals(line)) {channel.close(); // 调用close()方法是交给其他线程执行异步操作
//                    log.debug("处理关闭之后的操作"); // 不能在这里善后,因为是交给其他线程执行可能出现1s之后执行close()的情况break;}//输入的不是q则发送给服务器channel.writeAndFlush(line);}}, "input").start();// 获取 CloseFuture 对象, 1) 同步处理关闭, 2) 异步处理关闭ChannelFuture closeFuture = channel.closeFuture();//1) 同步处理关闭
//        log.debug("waiting close...");
//        closeFuture.sync();//只有在调用了channel.close()后才会继续向下运行
//        log.debug("处理关闭之后的操作");//2) 异步处理关闭System.out.println("closeFuture.getClass():"+closeFuture.getClass());//        channelFuture.addListener(new ChannelFutureListener() {
//            //关闭channel的线程调用operationComplete方法
//            //也就是nio线程执行完close(),找到该回调对象调用该方法
//            @Override
//            public void operationComplete(ChannelFuture future) throws Exception {
//                log.debug("处理关闭之后的操作");
//            }
//        });//以上代码会出现用户输入q时,程序没有结束的情况。// 原因是NioEventLoopGroup中还有一些线程在运行,需要进行关闭channelFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {log.debug("处理关闭之后的操作");group.shutdownGracefully();}});//或使用以下代码替换
//        closeFuture.addListener((ChannelFutureListener) future -> {
//            log.debug("处理关闭之后的操作");
//            group.shutdownGracefully();
//        });}
}

在这里插入图片描述

💡 netty异步提升的是什么

  • 有些同学看到这里会有疑问:为什么不在一个线程中去执行建立连接、去执行关闭 channel,那样不是也可以吗?非要用这么复杂的异步方式:比如一个线程发起建立连接,另一个线程去真正建立连接。
  • 还有同学会笼统地回答,因为 netty 异步方式用了多线程、多线程就效率高。其实这些认识都比较片面,多线程和异步所提升的效率并不是所认为的。(其实提高的是单位时间内处理请求的吞吐量)

思考:
思考下面的场景,4 个医生给人看病,每个病人花费 20 分钟,而且医生看病的过程中是以病人为单位的,一个病人看完了,才能看下一个病人。假设病人源源不断地来,可以计算一下 4 个医生一天工作 8 小时,处理的病人总数是:4 * 8 * 3 = 96
在这里插入图片描述
经研究发现,看病可以细分为四个步骤,经拆分后每个步骤需要 5 分钟,如下
在这里插入图片描述
因此可以做如下优化,只有一开始,医生 2、3、4 分别要等待 5、10、15 分钟才能执行工作,但只要后续病人源源不断地来,他们就能够满负荷工作,并且处理病人的能力提高到了 4 * 8 * 12 效率几乎是原来的四倍。
在这里插入图片描述

要点

  • 单线程没法异步提高效率,必须配合多线程、多核 cpu 才能发挥异步的优势
  • 异步并没有缩短响应时间,反而有所增加
  • 合理进行任务拆分,也是利用异步的关键

总结

异步并没有缩短处理单位的时间,反而有所增加请求的处理和响应时间。
关键点是提高了单位时间内处理请求的(个数)吞吐量,单位时间内能过处理请求的速度


全部文章:
Netty笔记01-Netty的基本概念与用法
Netty笔记02-组件EventLoop
Netty笔记03-组件Channel
Netty笔记04-组件Future & Promise
Netty笔记05-组件Handler & Pipeline

相关文章:

Netty笔记03-组件Channel

文章目录 Channel概述Channel 的概念Channel 的主要功能Channel 的生命周期Channel 的状态Channel 的类型channel 的主要方法 ChannelFutureCloseFuture&#x1f4a1; netty异步提升的是什么要点总结 Channel概述 Channel 的概念 在 Netty 中&#xff0c;Channel 是一个非常重…...

1----安卓机型修复串码 开启端口 檫除基带 支持高通与MTK机型工具预览与操作解析

在玩机过程中。很多玩家会碰到各种各样的故障 。其中最多的就在于基带 串码类。由于目前的安卓机型必须修改或者写入串码等参数必须开启端口。而一些初级玩友不太了解开启参数端口的步骤。这个工具很简单的为安卓机型开启端口。并且操作相对简单。 此工具基本功能 1-----可以…...

Docker容器技术1——docker基本操作

Docker容器技术 随着云计算和微服务架构的普及&#xff0c;容器技术成为了软件开发、测试和部署过程中的重要组成部分。其中&#xff0c;Docker作为容器技术的代表之一&#xff0c;以其简便易用的特点赢得了广大开发者的青睐。 Docker允许开发者在轻量级、可移植的容器中打包和…...

ElasticSearch介绍+使用

ElasticSearch 1.背景 ElasticSearch的最明显的优势在于其分布式特性&#xff0c;能够扩展到上百台服务器&#xff0c;极大地提高了服务器的容错率。在大数据时代背景下&#xff0c;ElasticSearch与传统的数据库相比较&#xff0c;能够应对大规模的并发搜索请求&#xff0c;同…...

Redis——常用数据类型List

目录 List列表常用命令lpushlpushxrpushrpushlrangelpoprpoplindexlinsertllenlremltrim key start stoplset 阻塞版本命令blpopbrpop list的编码方式list的应用 List列表 Redis中的list相当于数组&#xff0c;或者 顺序表&#xff0c;一些常用的操作可以通过下面这张图来理解…...

前端基础知识+算法(一)

文章目录 算法二分查找条件注意方式基本原理左闭右闭正向写法 左闭右开正向写法 前端基础知识定时器及清除盒子垂直水平居中的方式垂直水平1.flex布局2.grid布局3.定位对于块级元素 解决高度塌陷的方式1.给父元素一个固定的高度2.给父元素添加属性 overflow: hidden;3.在子元素…...

photozoom classic 9解锁码2024年最新25位解锁码

photozoom classic 9 破解版顾及比恐龙还要稀有&#xff0c;我曾经和你一样一直再找&#xff0c;找了好几个月&#xff0c;也没有找到真的破解版&#xff0c;下载很多次&#xff0c; 都是病毒插件之类的 我昨天下了几次&#xff0c;没有一个不附带插件病毒木马的.......&#x…...

Oracle发邮件功能:设置的步骤与注意事项?

Oracle发邮件配置教程&#xff1f;如何实现Oracle发邮件功能&#xff1f; Oracle数据库作为企业级应用的核心&#xff0c;提供了内置的发邮件功能&#xff0c;使得数据库管理员和开发人员能够通过数据库直接发送邮件。AokSend将详细介绍如何设置Oracle发邮件功能。 Oracle发邮…...

优化理论及应用精解【9】

文章目录 二次型函数二次型函数详细解释一、定义二、性质三、应用四、示例五、图表辅助说明&#xff08;由于文本限制&#xff0c;无法直接提供图表&#xff09; “西尔维斯特准则”一、定义二、来源三、应用场景 参考文献 二次型函数 二次型函数详细解释 一、定义 二次型函…...

nginx实现https安全访问的详细配置过程

文章目录 前言什么是 HTTP&#xff1f;什么是 HTTPS&#xff1f;HTTP 和 HTTPS 的区别为什么 HTTPS 被称为安全的&#xff1f;配置过程配置自签名证书 前言 首先我们来简单了解一下什么是http和https以及他们的区别所在. 什么是 HTTP&#xff1f; HTTP&#xff0c;全称为“超…...

1. TypeScript基本语法

TypeScript 学习总结 TypeScript 是一种 JavaScript 的超集&#xff0c;增加了静态类型检查和编译时错误检测&#xff0c;从而提高了代码的可维护性和可靠性。以下是 TypeScript 的基础知识总结&#xff0c;包括语法、运算符、数据类型、变量声明和作用域。 ## 基本语法TypeS…...

C# UDP与TCP点发【速发速断】模式

1、UDP 客户端 //由于收发都在本机&#xff0c;所以只用一个IP地址 IPAddress addr IPAddress.Parse("127.0.0.1"); var ptLocal new IPEndPoint(addr&#xff0c;9001);//本机节点&#xff0c;用于发送var ptDst new IPEndPoint(addr&#xff0c;9002);//目标节点…...

pikachu下

CSRF(跨站请求伪造) CSRF(get) url变成了这样了&#xff0c;我们就可以新开个页面直接拿url去修改密码 http://pikachu-master/vul/csrf/csrfget/csrf_get_login.php?username1&password2&submitLogin CSRF(post&#xff09; 这里只是请求的方式不同&#xff0c;…...

Go语言开发im-websocket服务和vue3+ts开发类似微信pc即时通讯

前言 IM即时通讯聊天, 为软件开发者打造&#xff0c;不依赖第三方sdk&#xff0c;完全用Go语言开发即时通讯服务&#xff0c;支持H5、Electron、Wails 、Uniapp和各种小程序的IM即时通讯, 快速实现私聊、群聊、在线客服&#xff01;让你快速搭建一个微信聊天系统&#xff0c;打…...

Redis如何实现分布式锁

目录 获取锁&#xff1a; 释放锁&#xff1a; Lua脚本&#xff1a; Redisson 分布式锁是&#xff0c;满足分布式系统或集群模式下多进程可见并且互斥的锁&#xff0c;因为我们熟知的java中的锁只是在单体架构下单个jvm中才会生效&#xff0c;如果部署了多个jvm则会导致新的…...

面向对象程序设计之继承(C++)

1.继承的定义 1.1继承的概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有类特性的基础上进⾏扩展&#xff0c;增加⽅法(成员函数)和属性(成员变量)&#xff0c;这样产⽣新的类&#xff0c;称派⽣类。继承 呈现了⾯向…...

IAPP发布《2024年人工智能治理实践报告》

文章目录 前言一、黑箱问题►透明度、可理解性与可解释性二、法律和政策中的注意事项►欧盟的《通用数据保护条例》►欧盟的AI法案►NIST的AI风险管理框架►美国的第14110号行政命令►《生成式人工智能服务管理暂行办法》►新加坡的AI验证三、实施人工智能治理►模型卡与系统卡…...

了解MySQL 高可用架构:主从备份

为了防止数据库的突然挂机&#xff0c;我们需要对数据库进行高可用架构。主从备份是常见的场景&#xff0c;通常情况下都是“一主一从/(多从)”。正常情况下&#xff0c;都是主机进行工作&#xff0c;从机进行备份主机数据&#xff0c;如果主机某天突然意外宕机&#xff0c;从机…...

[OpenCV] 数字图像处理 C++ 学习——15像素重映射(cv::remap) 附完整代码

文章目录 前言1.像素重映射理论基础2.代码实现(1) remap()细节(2)水平翻转(2)垂直翻转(3)旋转 180 度(4)径向扭曲 3.完整代码 前言 像素重映射将图像中的每个像素映射到新位置&#xff0c;实现图像的扭曲、校正等操作。在 OpenCV 中&#xff0c;cv::remap() 函数就是用于实现这…...

Oreace每日运维操作

一&#xff0e;Oreace每日运维操作 目录 一&#xff0e;Oreace每日运维操作 1.1、确认所有的INSTANCE状态正常 1.2、检查文件系统的使用&#xff08;剩余空间&#xff09; 1.3 lwh暗码&#xff0c;&#xff0c;、检查日志文件和trace文件记录 1.4 lwh、检查数据库当日备份…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案

在移动互联网营销竞争白热化的当下&#xff0c;推客小程序系统凭借其裂变传播、精准营销等特性&#xff0c;成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径&#xff0c;助力开发者打造具有市场竞争力的营销工具。​ 一、系统核心功能架构&…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...