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

Javacv-利用Netty实现推流直播复用(flv)

前言

上一篇文章《JavaCV之rtmp推流(FLV和M3U8)》介绍了javacv的基本使用,今天来讲讲如何实现推流复用。
以监控摄像头的直播为例,通常分为三步:

  1. 从设备获取音视频流
  2. 利用javacv进行解码(例如flv或m3u8)
  3. 将视频解码后数据推送到前端页面播放

推流直播复用,是指假如该设备某一个channel已经在解码直播了,其他channel只需要直接拿该设备解码后的视频帧数据进行播放即可,而无需重复上面三步。实现一次解码,多客户端播放。

什么是channel?

在Netty中,每个Channel实例代表一个与远程对等方的通信链接。在网络编程中,一个Channel通常对应于一个网络连接,可以是客户端到服务器的连接,也可以是服务器接受的客户端连接。

上述大概的推流复用流程如下图所示:

在这里插入图片描述

代码实例

MediaServer

负责创建Netty服务器。关键的步骤包括创建EventLoopGroup、配置ServerBootstrap、指定服务器的Channel类型为NioServerSocketChannel、设置服务器的处理器等。

这个服务器的实际处理逻辑是在LiveHandler类中实现的,这是一个自定义的ChannelHandler,它继承自SimpleChannelInboundHandler。在实际应用中,可以根据业务需求实现自己的ChannelHandler来处理接收到的消息。
这里维护了一个deviceContext设备容器,存放各个设备的TransferToFlv实例。

@Slf4j
@Component
public class MediaServer implements CommandLineRunner {@Autowiredprivate LiveHandler liveHandler;public static ConcurrentHashMap<String, TransferToFlv> deviceContext = new ConcurrentHashMap<>();public final static String  YOUR_VIDEO_PATH = "D:\灌篮高手.mp4";public final static int PORT = 8234;public void start() {InetSocketAddress socketAddress = new InetSocketAddress("0.0.0.0", PORT);//主线程组EventLoopGroup bossGroup = new NioEventLoopGroup(1);//工作线程组EventLoopGroup workGroup = new NioEventLoopGroup(200);ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) {CorsConfig corsConfig = CorsConfigBuilder.forAnyOrigin().allowNullOrigin().allowCredentials().build();socketChannel.pipeline().addLast(new HttpResponseEncoder()).addLast(new HttpRequestDecoder()).addLast(new ChunkedWriteHandler()).addLast(new HttpObjectAggregator(64 * 1024)).addLast(new CorsHandler(corsConfig)).addLast(liveHandler);}}).localAddress(socketAddress).option(ChannelOption.SO_BACKLOG, 128)//选择直接内存.option(ChannelOption.ALLOCATOR, PreferredDirectByteBufAllocator.DEFAULT).childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true).childOption(ChannelOption.SO_RCVBUF, 128 * 1024).childOption(ChannelOption.SO_SNDBUF, 1024 * 1024).childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024 / 2, 1024 * 1024));//绑定端口,开始接收进来的连接try {ChannelFuture future = bootstrap.bind(socketAddress).sync();future.channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//关闭主线程组bossGroup.shutdownGracefully();//关闭工作线程组workGroup.shutdownGracefully();}}@Overridepublic void run(String... args) {this.start();}
}

LiveHandler

继承于SimpleChannelInboundHandler,它是Netty中的一个特殊类型的Channel处理器,用于处理从通道中读取的数据,提供了一个简化的channelRead0方法,用于处理接收到的消息,而不必担心消息的释放。
这里实现的是判断请求地址是否为/live,并且获取地址中的deviceId,并将channel加入到设备的httpClients

@Service
@ChannelHandler.Sharable
public class LiveHandler extends SimpleChannelInboundHandler<Object> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) {FullHttpRequest req = (FullHttpRequest) msg;QueryStringDecoder decoder = new QueryStringDecoder(req.uri());// 判断请求uriif (!"/live".equals(decoder.path())) {sendError(ctx, HttpResponseStatus.BAD_REQUEST);return;}QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.uri());List<String> parameters = queryStringDecoder.parameters().get("deviceId");if(parameters == null || parameters.isEmpty()){sendError(ctx, HttpResponseStatus.BAD_REQUEST);return;}String deviceId = parameters.get(0);sendFlvResHeader(ctx);Device device = new Device(deviceId, MediaServer.YOUR_VIDEO_PATH);playForHttp(device, ctx);}public void playForHttp(Device device, ChannelHandlerContext ctx) {try {TransferToFlv mediaConvert = new TransferToFlv();if (MediaServer.deviceContext.containsKey(device.getDeviceId())) {mediaConvert = MediaServer.deviceContext.get(device.getDeviceId());mediaConvert.getMediaChannel().addChannel(ctx, true);return;}mediaConvert.setCurrentDevice(device);MediaChannel mediaChannel = new MediaChannel(device);mediaConvert.setMediaChannel(mediaChannel);MediaServer.deviceContext.put(device.getDeviceId(), mediaConvert);//注册事件mediaChannel.getEventBus().register(mediaConvert);new Thread(mediaConvert).start();mediaConvert.getMediaChannel().addChannel(ctx, false);} catch (InterruptedException | FFmpegFrameRecorder.Exception e) {throw new RuntimeException(e);}}/*** 错误请求响应** @param ctx* @param status*/private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,Unpooled.copiedBuffer("请求地址有误: " + status + "\r\n", CharsetUtil.UTF_8));response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);}/*** 发送req header,告知浏览器是flv格式** @param ctx*/private void sendFlvResHeader(ChannelHandlerContext ctx) {HttpResponse rsp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);rsp.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE).set(HttpHeaderNames.CONTENT_TYPE, "video/x-flv").set(HttpHeaderNames.ACCEPT_RANGES, "bytes").set(HttpHeaderNames.PRAGMA, "no-cache").set(HttpHeaderNames.CACHE_CONTROL, "no-cache").set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED).set(HttpHeaderNames.SERVER, "测试");ctx.writeAndFlush(rsp);}
}

MediaChannel

主要负责每个设备的channel添加、关闭,以及向channel发送数据。利用newScheduledThreadPool进行周期性检查channel的在线情况,如果全部channel下线,则使用事件总线eventBus通知关闭解码推流。

@Data
@AllArgsConstructor
public class MediaChannel {private Device currentDevice;public ConcurrentHashMap<String, ChannelHandlerContext> httpClients;private ScheduledFuture<?> checkFuture;private final ScheduledExecutorService scheduler;protected EventBus eventBus;public MediaChannel(Device currentDevice) {this.currentDevice = currentDevice;this.httpClients = new ConcurrentHashMap<>();this.scheduler = Executors.newScheduledThreadPool(1);this.eventBus = new EventBus();}public void addChannel(ChannelHandlerContext ctx, boolean needSendFlvHeader) throws InterruptedException, FFmpegFrameRecorder.Exception {if (ctx.channel().isWritable()) {ChannelFuture channelFuture = null;if (needSendFlvHeader) {//如果当前设备正在有channel播放,则先发送flvheader,再发送视频数据。byte[] flvHeader = MediaServer.deviceContext.get(currentDevice.getDeviceId()).getFlvHeader();channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer(flvHeader));} else {channelFuture = ctx.writeAndFlush(Unpooled.copiedBuffer(new ByteArrayOutputStream().toByteArray()));}channelFuture.addListener(future -> {if (future.isSuccess()) {httpClients.put(ctx.channel().id().toString(), ctx);}});this.checkFuture = scheduler.scheduleAtFixedRate(this::checkChannel, 0, 10, TimeUnit.SECONDS);System.out.println(currentDevice.getDeviceId() + ":channel:" + ctx.channel().id() + "创建成功");}Thread.sleep(50);}/*** 检查是否存在channel*/private void checkChannel() {if (httpClients.isEmpty()) {System.out.println("通知关闭推流");eventBus.post(this.currentDevice);this.checkFuture = null;scheduler.shutdown();}}/*** 关闭通道*/public void closeChannel() {for (Map.Entry<String, ChannelHandlerContext> entry : httpClients.entrySet()) {entry.getValue().close();}}/*** 发送数据** @param data*/public void sendData(byte[] data) {for (Map.Entry<String, ChannelHandlerContext> entry : httpClients.entrySet()) {if (entry.getValue().channel().isWritable()) {entry.getValue().writeAndFlush(Unpooled.copiedBuffer(data));} else {httpClients.remove(entry.getKey());System.out.println(currentDevice.getDeviceId() + ":channel:" + entry.getKey() + "已被去除");}}}}

TransferToFlv

流的解码、推送部分就是在这个类里面,使用的是javacv封装的ffmpeg库,将音视频流转换为flv格式。实际的参数可以根据业务调整。
这里增加了一个获取flv格式header数据方法,因为flv格式视频必须要包含flv header才能播放。复用推流数据的时候,先向前端发送flv格式header,再发送流数据。

@Slf4j
@Data
public class TransferToFlv implements Runnable {private volatile boolean running = false;private FFmpegFrameGrabber grabber;private FFmpegFrameRecorder recorder;public ByteArrayOutputStream bos = new ByteArrayOutputStream();private Device currentDevice;private MediaChannel mediaChannel;public ConcurrentHashMap<String, ChannelHandlerContext> httpClients = new ConcurrentHashMap<>();/*** 创建拉流器** @return*/protected void createGrabber(String url) throws FFmpegFrameGrabber.Exception {grabber = new FFmpegFrameGrabber(url);//拉流超时时间(10秒)grabber.setOption("stimeout", "10000000");grabber.setOption("threads", "1");grabber.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);// 设置缓存大小,提高画质、减少卡顿花屏grabber.setOption("buffer_size", "1024000");// 读写超时,适用于所有协议的通用读写超时grabber.setOption("rw_timeout", "15000000");// 探测视频流信息,为空默认5000000微秒// grabber.setOption("probesize", "5000000");// 解析视频流信息,为空默认5000000微秒//grabber.setOption("analyzeduration", "5000000");grabber.start();}/*** 创建录制器** @return*/protected void createTransterOrRecodeRecorder() throws FFmpegFrameRecorder.Exception {recorder = new FFmpegFrameRecorder(bos, grabber.getImageWidth(), grabber.getImageHeight(),grabber.getAudioChannels());setRecorderParams(recorder);recorder.start();}/*** 设置录制器参数** @param fFmpegFrameRecorder*/private void setRecorderParams(FFmpegFrameRecorder fFmpegFrameRecorder) {fFmpegFrameRecorder.setFormat("flv");// 转码fFmpegFrameRecorder.setInterleaved(false);fFmpegFrameRecorder.setVideoOption("tune", "zerolatency");fFmpegFrameRecorder.setVideoOption("preset", "ultrafast");fFmpegFrameRecorder.setVideoOption("crf", "23");fFmpegFrameRecorder.setVideoOption("threads", "1");fFmpegFrameRecorder.setFrameRate(25);// 设置帧率fFmpegFrameRecorder.setGopSize(25);// 设置gop,与帧率相同//recorder.setVideoBitrate(500 * 1000);// 码率500kb/sfFmpegFrameRecorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);fFmpegFrameRecorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);fFmpegFrameRecorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);fFmpegFrameRecorder.setOption("keyint_min", "25");  //gop最小间隔fFmpegFrameRecorder.setTrellis(1);fFmpegFrameRecorder.setMaxDelay(0);// 设置延迟}/*** 获取flv格式header数据** @return* @throws FFmpegFrameRecorder.Exception*/public byte[] getFlvHeader() throws FFmpegFrameRecorder.Exception {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();FFmpegFrameRecorder fFmpegFrameRecorder = new FFmpegFrameRecorder(byteArrayOutputStream, grabber.getImageWidth(), grabber.getImageHeight(),grabber.getAudioChannels());setRecorderParams(fFmpegFrameRecorder);fFmpegFrameRecorder.start();return byteArrayOutputStream.toByteArray();}/*** 将视频源转换为flv*/protected void transferToFlv() {//创建拉流器try {createGrabber(currentDevice.getRtmpUrl());//创建录制器createTransterOrRecodeRecorder();grabber.flush();running = true;// 时间戳计算long startTime = 0;long lastTime = System.currentTimeMillis();while (running) {// 转码Frame frame = grabber.grab();if (frame != null && frame.image != null) {lastTime = System.currentTimeMillis();recorder.setTimestamp((1000 * (System.currentTimeMillis() - startTime)));recorder.record(frame);if (bos.size() > 0) {byte[] b = bos.toByteArray();bos.reset();sendFrameData(b);continue;}}//10秒内读不到视频帧,则关闭连接if ((System.currentTimeMillis() / 1000 - lastTime / 1000) > 10) {System.out.println(currentDevice.getDeviceId() + ":10秒内读不到视频帧");break;}}} catch (FFmpegFrameRecorder.Exception | FrameGrabber.Exception e) {throw new RuntimeException(e);} finally {try {recorder.close();grabber.close();bos.close();closeMedia();} catch (IOException e) {throw new RuntimeException(e);}}}/*** 发送帧数据** @param data*/private void sendFrameData(byte[] data) {mediaChannel.sendData(data);}/*** 关闭流媒体*/private void closeMedia() {running = false;MediaServer.deviceContext.remove(currentDevice.getDeviceId());mediaChannel.closeChannel();}/*** 通知关闭推流** @param device*/@Subscribepublic void checkChannel(Device device) {if (device.getDeviceId().equals(currentDevice.getDeviceId())) {closeMedia();System.out.println("关闭推流完成");}}@Overridepublic void run() {transferToFlv();}}

演示

前端就简单用flv.js进行演示,首次进行设备1和设备2播放,都需要进行解码推流,当设备1建立一个新channel(第三个视频画面)进行播放时,只需拿前面的第一个channel数据即可,无需进行再次进行解码。

image.png
可以看出,第三个视频播放的时候,跟第一个视频画面进度是同步的。

结束

附上代码地址: https://gitee.com/zhouxiaoben/keep-learning.git
这次分享就到这,大家有什么好的优化建议可以放在评论区。

相关文章:

Javacv-利用Netty实现推流直播复用(flv)

前言 上一篇文章《JavaCV之rtmp推流&#xff08;FLV和M3U8&#xff09;》介绍了javacv的基本使用&#xff0c;今天来讲讲如何实现推流复用。 以监控摄像头的直播为例&#xff0c;通常分为三步&#xff1a; 从设备获取音视频流利用javacv进行解码&#xff08;例如flv或m3u8&am…...

cfa一级考生复习经验分享系列(十一)

理工科已经毕业&#xff0c;正在工作&#xff0c;毫无金融背景。一共准备了四个月&#xff0c;每天下班和周末抽时间看看。前三个月节奏比较松散&#xff0c;毕竟时不时有人叫我出去high&#xff0c;最后一个月认真看了看。 用到的资料 JC网课&#xff0c;官方Mock&#xff0c;…...

Nginx基本配置内容

http 模块适用于处理 Web 请求&#xff0c;而 stream 模块适用于处理非 HTTP 流量&#xff0c;如数据库连接、邮件传输等。 在 stream 模块中&#xff0c;你可以配置一些 TCP 或 UDP 的代理服务&#xff0c;以便 Nginx 能够转发这些流量。 与网站相关的 关于网站相关的要写在ht…...

Jenkins安装与设置(插件安装失败,版本问题解决)

早期的使用docker安装jenkins的方法会出现插件无法安装的问题&#xff0c;是由于docker拉取的jenkins版本太低了 jdk安装 Linux系统安装JDK1.8 详细流程 maven安装&#xff1a; centos7下安装Maven 使用docker进行安装jenkins&#xff1a; 先把镜像和容器卸干净 docker ps -a…...

精度提升10个点!HD-Painter:无需训练的文本引导高分辨率图像修复方案!

基于文本到图像扩散模型的空前成功&#xff0c;在文本引导的图像修复方面取得了最新进展&#xff0c;取得了异常逼真和视觉上可靠的结果。然而&#xff0c;目前的文本到图像修复模型仍然存在显著的改进潜力&#xff0c;特别是在更好地与用户提示对齐和执行高分辨率修复方面。因…...

javaweb初体验

javaweb初体验 文章目录 javaweb初体验前言一、流程&#xff1a;1.创建Maven的父工程2.创建Maven&#xff0c;Webapp的子工程3.在pom.xml文件中添加依赖&#xff08;父工程与子工程共用&#xff09;4.写一个helloservlet类实现httpservlet接口&#xff0c;重写doget&#xff0c…...

手写爬虫框架

前言 参照了Scrapy、Feapder的设计模式&#xff0c;实现的一个轻量级爬虫框架&#xff08;目前约200行代码&#xff09; 源码地址 https://gitee.com/markadc/pader 项目持续更新中…...

基于Kettle和帆软Finereport的血缘解析

一、背景&#xff1a; 用户经常会针对数据存在质量的存疑&#xff0c;反馈数据不准。开发人员排查数据质量问题步骤&#xff1a;首先和业务人员对接了解是哪里数据不准确&#xff0c;要定位是哪张报表&#xff0c;然后查看报表后面数据来源&#xff0c;然后一路排查数仓。往往定…...

给qemu虚机更换(Windows PE)光盘

1. 背景 qemu虚机里运行windows。如果遇到虚机windows启动故障&#xff0c;甚至连安全模式也故障时&#xff0c;可以尝试更换另一个光驱里的光盘为pe光盘。 2. 步骤 2.1. 找出VDI虚机所在的计算节点 ssh登录之&#xff0c;virsh list获得虚机id&#xff0c;例如 391 1255…...

python 神经网络归纳

CNN卷积神经网络 一个卷积神经网络主要由以下5层组成&#xff1a; 数据输入层/ Input layer卷积计算层/ CONV layerReLU激励层 / ReLU layer池化层 / Pooling layer全连接层 / FC layer 1. 数据输入层 该层要做的处理主要是对原始图像数据进行预处理&#xff0c;其中包括&…...

Python高级语法与正则表达式

Python提供了 with 语句的写法&#xff0c;既简单又安全。 文件操作的时候使用with语句可以自动调用关闭文件操作&#xff0c;即使出现异常也会自动关闭文件操作。 # 1、以写的方式打开文件 with open(1.txt, w) as f:# 2、读取文件内容f.write(hello world) 生成器的创建方…...

【洛谷算法题】P4414-[COCI2006-2007#2] ABC【入门2分支结构】Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P4414-[COCI2006-2007#2] ABC【入门2分支结构】Java题解&#x1f30f;题目描述&a…...

Python如何将图片转换成字符

PIL(Python Image Library)库是Python平台上一个功能强大的图像处理标准库&#xff0c;支持图像的存储、显示和处理&#xff0c;几乎可以处理所有图片格式&#xff0c;如图像的压缩、裁剪、叠加、添加文字等等。 安装PIL库:pip install pillow from PIL import Image ascii_cha…...

国家开放大学形成性考核 统一资料 参考试题

试卷代号&#xff1a;1174 水工钢筋混凝土结构&#xff08;本&#xff09;参考试题 一、选择题&#xff08;每小题2分&#xff0c;共20分&#xff0c;在所列备选项中&#xff0c;选1项正确的或最好的作为答案&#xff0c;将选项号填入各题的括号中&#xff09; 1.钢筋混凝土结…...

4.7 【共享源】流的生产者(二)

七,模式 流的模式决定了Screen如何使前台缓冲区可用。生产者通过调用screen_set_stream_property_iv()并设置SCREEN_PROPERTY_MODE属性来设置模式。有效模式如下: 7.1 SCREEN_STREAM_MODE_DEFAULT 如果生产者应用程序没有在流上明确设置 SCREEN_PROPERTY_MODE 属性,则 Sc…...

流量录制回放工具在自动化测试领域应用探索

引言&#xff1a; 随着中国农业银行技术架构的日益更迭与业务场景的不断创新&#xff0c;测试工作正在面临数据构造繁琐、案例维护成本较高且质量参差不齐等诸多问题与挑战&#xff0c;主要体现在以下四方面&#xff1a; 一是在系统架构升级与代码重构时&#xff0c;大量原始接…...

【高数定积分求解旋转体体积】 —— (上)高等数学|定积分|柱壳法|学习技巧

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 Shell method Setting up the Integral 例题 Example 1: Example 2: Example 3: Example …...

Ubuntu20.04 及深度学习环境anaconda、cuda、cudnn、pytorch、paddle2.3安装记录

学习目标&#xff1a; Ubuntu20.04下装好torch、paddle深度学习环境。 选择的版本环境是 &#xff1a;最新的nvidia驱动、cuda 11.1 、cudnn v8.1.1&#xff0c;下面会说为啥这么选。 学习内容&#xff1a; 1. Ubuntu20.04仓库换源 本节参考Ubuntu 20.04 Linux更换源教程 2…...

场景切割CVPr2022 SceneSegmentation

目录 算法介绍 无监督训练原理 源码地址: lstm模块 bilstm opencv场景分割 加阈值:...

Spring Cloud Feign作为HTTP客户端调用远程HTTP服务

如果你的项目使用了SpringCloud微服务技术,那么你就可以使用Feign来作为http客户端来调用远程的http服务。当然,如果你不想使用Feign作为http客户端,也可以使用比如JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client或者Spring的RestTemplate。 那么,为…...

[node] Node.js的文件系统

[node] Node.js的文件系统 文件系统的使用异步和同步input.txt示例 常用方法打开文件语法示例 获取文件信息语法示例 写入文件语法示例 读取文件语法示例 关闭文件语法示例 截取文件语法示例 删除文件语法示例 创建目录语法示例 读取目录语法示例 删除目录语法示例 文件模块方法…...

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…...

CJson 使用 - 解析Object结构

简介 准备在开发板中使用json结构传送数据, 选用了cJson, 现在看下cJson的使用吧步骤 下载 git clone https://github.com/DaveGamble/cJSON 或者直接压缩包下载也行, 毕竟国内有时候下载不下来Qt 中使用cJson 在下载的cJson 目录中加入cJson.pri, 内容如下 INCLUDEPATH …...

MySQL8主主搭建

-- mysql8 主主搭建 mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz 主1 &#xff1a; 192.168.2.160 主2 &#xff1a; 192.168.2.161 --解压mysql-8.0.35-linux-glibc2.12-x86_64.tar.xz为mysql8 -- 初始化mysql8 &#xff08;略&#xff09; -- 参数192.168.2.160 [root…...

使用Pycharm一键将.ui文件生成.py文件配置教程、一键打开QTDesigner教程

2df3621a-7ffd-4f18-9735-b86464b83a5b 前言 我痛恨所有将白嫖归为理所应当的猪&#x1f416;。 教程 打开pycharm之后&#xff0c;依次点击File->Settings->Tools->External Tools&#xff0c;进入如下界面&#xff1a; 1、配置快捷打开Qt Designer 点击号&…...

React 路由跳转

1. push 与 replace 模式 默认情况下&#xff0c;开启的是 push 模式&#xff0c;也就是说&#xff0c;每次点击跳转&#xff0c;都会向栈中压入一个新的地址&#xff0c;在点击返回时&#xff0c;可以返回到上一个打开的地址&#xff0c; 就像上图一样&#xff0c;我们每次返…...

【MySQL】数据处理:从SQL批量删除报错到Python优雅解决

一、背景 MySQL数据库表中有一批重复的脏数据,为不影响正常业务,需要进行批量删除。 二、SQL批量删除 首先想到的是编写SQL语句来批量删除:删除身份证号为51****59且ID不为5的全部数据(保留ID为5的那一条数据) DELETE FROM test_table WHERE id IN ( SELECT i…...

Rust 软件测试

Rust 第18节 软件测试 测试已写的函数 在创建每一个lib crate时&#xff0c;rust都会自动生产一个测试 mod;名字为tests; tests mod 被属性 #[cfg(test)] 修饰&#xff0c;用于测试&#xff1b; 在mod 内&#xff0c;需要在函数头上加属性 #[test]进行修饰&#xff0c;让其变为…...

win11下配置visual studio 2022+PCL1.13.1

第一部分&#xff1a;visual studio2022 安装 vs官网网址如下&#xff1a;https://visualstudio.microsoft.com/zh-hans/vs/ 第一步&#xff1a;我们打开官网链接&#xff0c;按如下操作点击下载免费版本的exe文件 第二步&#xff1a;打开下载目录下的安装文件进行安装&#…...

智能优化算法应用:基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于法医调查算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.法医调查算法4.实验参数设定5.算法结果6.…...

【分享】4个方法打开PDF文件

PDF是很多人工作中经常使用的电子文档格式&#xff0c;但是可能有些刚接触的小伙伴不知道用什么工具来打开PDF文件&#xff0c;今天小编就来分享一下4种常用的工具。 1. 使用浏览器 只要有电脑基本都会安装一到两款浏览器&#xff0c;其实浏览器也可以用来打开PDF文件。 只需…...

React 调试

React 调试 console 调试 console.log console.info console.warn console.error console.time() & console.timeEnd() console.count() debugger调试 chrome断点调试 chrome条件断点 HTML节点复制 选中节点&#xff0c;使用 $0复制 JavaScript对象复制 copy…...

List 流的使用

摘要 本文将介绍在 Java 1.8 中对 List 进行流操作的使用方法。引入的 java.util.stream 包为开发者提供了一种更为便捷和强大的方式来处理集合数据。通过使用流&#xff0c;我们能够以声明性的方式进行集合操作&#xff0c;减少了样板代码&#xff0c;提高了代码的可读性和可…...

设计模式:单例模式(使用c++分别实现饿汉式单例、带双重检查锁定机制的懒汉式单例以及IoDH技术的单例)

单例模式 引言正文1. 饿汉式单例2. 带双重检查锁定机制的懒汉式单例3. IoDH技术的单例 结论优点饿汉式单例带双重检查锁定机制的懒汉式单例IoDH技术的单例 缺点饿汉式单例带双重检查锁定机制的懒汉式单例IoDH技术的单例 引言 在软件开发中&#xff0c;单例模式是一种很常用的设…...

[编程相关]正则表达式Regex语法

--目录-- 0. 前言1. 正则语法2. 正则搜索语法&#xff08;1&#xff09;字符集 Character_Classes&#xff08;2&#xff09;锚定符 Anchors&#xff08;3&#xff09;计数 Quantifiers&#xff08;4&#xff09;分组与索引 Group_And_Reference&#xff08;5&#xff09;周围 …...

axios实例配置和使用

一.vite项目中引入axios 1.1.安装axios pnpm add axios --save 二.配置axios实例 2.1实例配置 import axios from ‘axios’ import router from ‘/router’ const instance axios.create({ baseURL:“http://127.0.0.1:8080”, timeout:10*1000, //最长响应时间 }) instan…...

uni-app 工程目录结构介绍

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…...

YOLOv8改进 | 主干篇 | 利用SENetV2改进网络结构 (全网首发改进)

一、本文介绍 本文给大家带来的改进机制是SENetV2&#xff0c;其是2023.11月的最新机制(所以大家想要发论文的可以在上面下点功夫)&#xff0c;其是一种通过调整卷积网络中的通道关系来提升性能的网络结构。SENet并不是一个独立的网络模型&#xff0c;而是一个可以和现有的任何…...

TUP实现一对一聊天

package TCP; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; /** * 发送消息线程 */ class Send ext…...

Kafka设计原理详解

Kafka核心总控制器 (Controller) 在Kafka集群中&#xff0c;通常会有一个或多个broker&#xff0c;其中一个会被选举为控制器 (Kafka Controller)&#xff0c;其主要职责是管理整个集群中所有分区和副本的状态。具体来说&#xff1a; 当某个分区的leader副本出现故障时&#…...

光耦继电器

光耦继电器(光电继电器) AQW282SX 282SZ 280SX 280SZ 284SX 284SZ 212S 212SX 21 2SZ 文章目录 光耦继电器(光电继电器)前言一、光耦继电器是什么二、光耦继电器的类型三、光电耦合器的应用总结前言 光耦继电器在工业控制、通讯、医疗设备、家电及汽车电子等领域得到广泛应…...

【C++练级之路】【Lv.5】动态内存管理(都2023年了,不会有人还不知道new吧?)

目录 一、C/C内存分布二、new和delete的使用方式2.1 C语言内存管理2.2 C内存管理2.2.1 new和delete操作内置类型2.2.2 new和delete操作自定义类型 三、new和delete的底层原理3.1 operator new与operator delete函数3.2 原理总结3.2.1 内置类型3.2.2 自定义类型 四、定位new表达…...

2016年第五届数学建模国际赛小美赛A题臭氧消耗预测解题全过程文档及程序

2016年第五届数学建模国际赛小美赛 A题 臭氧消耗预测 原题再现&#xff1a; 臭氧消耗包括自1970年代后期以来观察到的若干现象&#xff1a;地球平流层&#xff08;臭氧层&#xff09;臭氧总量稳步下降&#xff0c;以及地球极地附近平流层臭氧&#xff08;称为臭氧空洞&#x…...

springMVC-与spring整合

一、基本介绍 在项目开发中&#xff0c;spring管理的 Service和 Respository&#xff0c;SrpingMVC管理 Controller和ControllerAdvice,分工明确 当我们同时配置application.xml, springDispatcherServlet-servlet.xml , 那么注解的对象会被创建两次&#xff0c; 故…...

【二叉树】【单调双向队列】LeetCode239:滑动窗口最大值

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 涉及知识点 单调双向队列 二叉树 题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动…...

如何使用树莓派Bookworm系统中配置网络的新方法NetworkManager

树莓派在 10 月新出的 Bookworm 版本系统中&#xff0c;将使用多年的 dhcpcd 换成了 NetworkManager&#xff08;以前是在rasp-config中可选&#xff09;&#xff0c;这是因为 Raspberry Pi OS 使用的是 Debian 内核&#xff08;和 Ubuntu 一样&#xff09;&#xff0c;所以树莓…...

恶意软件分析沙箱在网络安全策略中处于什么位置?

恶意软件分析沙箱提供了一种全面的恶意软件分析方法&#xff0c;包括静态和动态技术。这种全面的评估可以更全面地了解恶意软件的功能和潜在影响。然而&#xff0c;许多组织在确定在其安全基础设施中实施沙箱的最有效方法方面面临挑战。让我们看一下可以有效利用沙盒解决方案的…...

ARM学习(24)Can的高阶认识和错误处理

笔者来聊一下CAN协议帧的认识和错误处理。 1、CAN协议帧认识 CAN 差分信号&#xff0c;是经过CAN收发器转成差分信号的&#xff0c;CAN RX和TX是逻辑电平。CAN的基础知识&#xff0c;可参考笔者这边文章&#xff1a;ARM学习&#xff08;21&#xff09;STM32 外设Can的认识与驱…...

网络通信--深入理解网络和TCP / IP协议

计算机网络体系结构 TCP/IP协议族 TCP / IP 网络传输中的数据术语 网络通信中的地址和端口 window端查看IP地址和MAC地址&#xff1a;ipconfig -all MAC层地址是在数据链路层的&#xff1b;IP工作在网络层的 MAC是48个字节&#xff0c;IP是32个字节 在子网&#xff08;局域…...

IPC之九:使用UNIX Domain Socket进行进程间通信的实例

socket 编程是一种用于网络通信的编程方式&#xff0c;在 socket 的协议族中除了常用的 AF_INET、AF_RAW、AF_NETLINK等以外&#xff0c;还有一个专门用于 IPC 的协议族 AF_UNIX&#xff0c;IPC 是 Linux 编程中一个重要的概念&#xff0c;常用的 IPC 方式有管道、消息队列、共…...