springboot框架使用Netty依赖中解码器的作用及实现详解
在项目开发 有需求 需要跟硬件通信
也没有mqtt 作为桥接 也不能http 请求 api 所以也不能 json字符串这么爽传输
所以要用tcp 请求 进行数据交互 数据还是16进制的 写法 有帧头 什么的
对于这种物联网的这种对接
我的理解就是 我们做的工作就像翻译
把这些看不懂的 字节流 变成 java 认识的数据
就了解了一下 netty 这个依赖
在springboot 项目种使用这个依赖进行 连接
但是这个依赖 如果不熟悉 使用不当 有可能有内存溢出的风险
所以大家如果自己用这个netty 依赖 做物联网的调用
要小心 最好能找一下 物联网中间件 基于netty封装的
这是我后续 找的一个中间件 也不算很成熟 如果大家有更好用的 欢迎留言 一起分享
http://www.iteaj.com/#/course
但是我发现还是要根据 对接的物联网硬件文档 写自定义解码器 或者编码器
这篇文章主要分享一下 我了解到的 netty 下的 四个解码器 使用方案
概念:
拆包和沾包
是典型的拆包和沾包问题,俗话说就是两端通信,一端发送一端接收,接收的那一端怎么知道是否已经完整的接收了数据?
假设服务端连续发送了两条消息:hello world! / hello client!
由于客户端不知道怎么才算一条消息,怎么才算两条消息,所以读取会有以下几种情况:
1.分两次读取消息,第一次是hello world!,第二次是hello client! 这是正常情况
2.一次就读取完成,hello world!hello client! 这种情况就叫沾包
3.分两次读取消息,第一次是hello ,第二次是world!hello client! 这第一次读取就是拆包,第二次就是沾包
总之就是读取到的信息不完整就是拆包,读取到的信息有额外多的信息就是沾包
解码器就可以来解决这个问题
依赖引入就不说了
先分享代码
netty配置类
package com.netty.server.tpcServer;import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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 io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** User:Json* Date: 2024/4/12**/
@Component
@Slf4j
public class NettyServer {private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);// 服务端NIO线程组private final EventLoopGroup bossGroup = new NioEventLoopGroup();private final EventLoopGroup workGroup = new NioEventLoopGroup();public ChannelFuture start(String host, int port) {ChannelFuture channelFuture = null;try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 1024 就是规定传入的 字节长度的大小// 对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解//第一种:LineBasedFrameDecoder:传入的参数是消息最大长度,发送消息的大小必须小于设置值// 行分隔符解码器(结尾根据 “\n” 作为结束标识)//socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));//第二种:DelimiterBasedFrameDecoder 自定义分割器解码器,结尾根据什么作为结束标识可以自定义 比如用 |// socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("|".getBytes())));// 第三种: FixedLengthFrameDecoder // 固定长度解码器,发送的消息需要定长// socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(7));// 上面三个解码器明显的看到不够灵活 所以还有第四种//第四种:LengthFieldBasedFrameDecoder:基于长度的自定义解码器,比较灵活 这个// 这个 解码器 一共有 5个参数
// maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃
// lengthFieldOffset:长度域偏移量。存储数据长度的一个偏移量
// lengthFieldLength:长度域字节数。存储数据长度的一个大小
// lengthAdjustment:数据长度修正。因为长度既可以代表data的长度,也可以是整个消息的长度
// initialBytesToStrip:跳过的字节数。可以选择舍弃一部分数据// 可以理解为 我们给数据定义传输规则 就像http请求 定义规则 比如 请求头 数据结构 json 等等// 所以我们作为接收数据的服务端 可以在开发接收数据的时候 定义规则 然后让客户端按照规则来传输数据// 比如 发送一个消息,我们定义的结构为:消息头+数据长度+数据// 我们怎么定义规则呢 比如:// 整体字节为 15个字节// 要发送的数据 "Message" 长度为 7个字节//我们规定 maxFrameLength :1024 接收的最大数据,// lengthFieldOffset:长度域偏移量 一般用来定义消息头 4个字节、// lengthFieldLength:长度域字节数 用于定义数据长度(length)的【值】的大小 4个字节// lengthAdjustment : 0 数据长度修正// 假设我设置的数据长度是20,代表了整个消息体的长度,但是我数据却只有12个字节,这往后读20个字节无疑是错的,所以我们需要修正,怎么修正? 减8 就行// 所以如果你需要修正你的 数据长度,那么lengthAdjustment就是用来修正的。// initialBytesToStrip :8 ,如果在我们业务层只需要 消息体 像消息头 和 数据长度都不需要 那么 initialBytesToStrip就是用来跳过的字节数。// 就比如 消息头规定 4个字节 数据长度 4个字节 所以我们如果只要 消息体里的内容 就需要跳过 8个字节 所以设置8 即可// 获取全部数据// socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,0));// 只获取消息体里的数据socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,8));// 自定义服务处理 所以再进行服务处理之前 需要先经历 解码器 既然有解码器 也有编码器// 如果我们作为服务端 那就需要编码器 如果我们作为客户端 那就需要解码器// 解码器主要用于 将接收到的字节数据转换成有意义的业务对象。比如java 认识的 实体类对象// 在网络编程中,数据在传输过程中通常是以字节数组的形式进行传递的,// 而业务处理通常需要对这些字节数组进行解析,转换为具体的业务对象。解码器正是完成这一工作的组件// 而且解码器 还需要 处理拆包和沾包 等 所以我们在使用 tcp 进行通信的时候 到业务层处理 的时候 需要先经过解码器socketChannel.pipeline().addLast(new ServerHandler());}});// 绑定端口并同步等待channelFuture = bootstrap.bind(host, port).sync();log.info("======Start Up Success!=========");} catch (Exception e) {e.printStackTrace();}return channelFuture;}public void close() {workGroup.shutdownGracefully();bossGroup.shutdownGracefully();log.info("======Shutdown Netty Server Success!=========");}//测试 mainpublic static void main(String[] args) throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new StringEncoder());}});ChannelFuture channelFuture = bootstrap.connect("192.168.0.209", 17070).sync();Channel channel = channelFuture.channel();// 连续发送多条消息for (int i = 0; i < 100; i++) {//System.getProperty("line.separator") 获取系统换行符 测试一种情况// String message = "Message " + i + System.getProperty("line.separator");// String message = "Message " + i+"|"; //测试第二种情况// String message = "Message"; // 第三种 固定长度情况// channel.writeAndFlush(message);//第四种 自定义解码器//[4个字节 + 4个字节 + 数据]// 我们传输数据 就要这样拼接数据 消息头 +数据长度+数据String message = "Message"+i;//消息头 4个字节ByteBuf byteBuf = Unpooled.buffer();byteBuf.writeInt(102); //【lengthFieldOffset】 4个字节// 102 测试随便写的 只要是占4个字节的数字就行 为啥写个102就4个字节 因为102 是int类型 int类型的字节数是4个字节// 这个应该是 数据类型基础 int string long 等等 占几个字节的问题//数据长度 4个字节 长度也是 int型 所以跟消息头 一样 所以在设置解码器长度参数的时候 【lengthFieldLength】 4个字节就够用byteBuf.writeInt(message.getBytes().length);//发送的数据byteBuf.writeBytes(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8));channel.writeAndFlush(byteBuf);// Thread.sleep(100); // 可能需要调整间隔时间}channel.closeFuture().sync();} finally {group.shutdownGracefully();}}
}
回调类
package com.netty.server.tpcServer;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;import java.text.SimpleDateFormat;
import java.util.Date;/*** User:Json* Date: 2024/4/12**/
public class ServerHandler extends ChannelInboundHandlerAdapter {/*** 客户端数据到来时触发*/@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf) msg;//第一种到第三种解码器测试
// System.out.println("client request: " + buf.toString(CharsetUtil.UTF_8)+"======");
// SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
// String callback = sf.format(new Date());
// ctx.write(Unpooled.copiedBuffer(callback.getBytes()));//第四种 自定义解码器测试System.out.println("========================");// 获取全部数据
// System.out.println("消息头:"+buf.readInt());
// int i = buf.readInt();
// System.out.println("数据长度:"+i);
// System.out.println("消息体:"+buf.readBytes(i).toString(CharsetUtil.UTF_8));//只获取消息体里的数据System.out.println("只获取消息体: "+buf.toString(CharsetUtil.UTF_8));}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// 将发送缓冲区的消息全部写到SocketChannel中ctx.flush();}/*** 发生异常时触发*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println(cause.getMessage());cause.printStackTrace();// 释放与ChannelHandlerContext相关联的资源ctx.close();}
}
springboot 启动时运行
package com.netty.server.init;import com.netty.server.tpcServer.NettyServer;
import io.netty.channel.ChannelFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import javax.annotation.Resource;/*** User:Json* Date: 2024/4/12**/
@Component
@Slf4j
@Order(2)
public class NettyServerInit implements CommandLineRunner {@Resourceprivate NettyServer nettyServer;// 这个 线程 后续优化 比如关闭 等等@Overridepublic void run(String... args) throws Exception {// 开启服务ChannelFuture future = nettyServer.start("192.168.0.209", 17070);// 在JVM销毁前关闭服务Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {nettyServer.close();}});future.channel().closeFuture().sync();}
}
核心测试代码 在 NettyServer 类里
下面逐个分享一下解码器的 意思 :
第一种:
new LineBasedFrameDecoder(1024)
1024 就是规定传入的 字节长度的大小
对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解
LineBasedFrameDecoder:传入的参数是消息最大长度,发送消息的大小必须小于设置值
行分隔符解码器(结尾根据 “\n” 作为结束标识)
第二种:
new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("|".getBytes()))
DelimiterBasedFrameDecoder 自定义分割器解码器,结尾根据什么作为结束标识可以自定义 比如用 |
第三种:
new FixedLengthFrameDecoder(7)
// 固定长度解码器,发送的消息需要定长
第四种:重点
new LengthFieldBasedFrameDecoder(1024,4,4,0,0)
第四种:LengthFieldBasedFrameDecoder:基于长度的自定义解码器,比较灵活 这个
这个 解码器 一共有 5个参数
maxFrameLength:最大帧长度。也就是可以接收的数据的最大长度。如果超过,此次数据会被丢弃
lengthFieldOffset:长度域偏移量。存储数据长度的一个偏移量
lengthFieldLength:长度域字节数。存储数据长度的一个大小
lengthAdjustment:数据长度修正。因为长度既可以代表data的长度,也可以是整个消息的长度
initialBytesToStrip:跳过的字节数。可以选择舍弃一部分数据
可以理解为 我们给数据定义传输规则 就像http请求 定义规则 比如 请求头 数据结构 json 等等
所以我们作为接收数据的服务端 可以在开发接收数据的时候 定义规则 然后让客户端按照规则来传输数据
比如 发送一个消息,我们定义的结构为:消息头+数据长度+数据
对于字节 一个字符串占多少字节 int 占多多少字节 long 占多多少字节 如果明白这个 应该很好理解
我们怎么定义规则呢 比如:
整体字节为 15个字节
要发送的数据 “Message” 长度为 7个字节
我们规定 maxFrameLength :1024 接收的最大数据,
lengthFieldOffset:长度域偏移量 一般用来定义消息头 4个字节、
lengthFieldLength:长度域字节数 用于定义数据长度(length)的【值】的大小 4个字节
lengthAdjustment : 0 数据长度修正
假设我设置的数据长度是20,代表了整个消息体的长度,但是我数据却只有12个字节,这往后读20个字节无疑是错的,所以我们需要修正,怎么修正? 减8 就行
所以如果你需要修正你的 数据长度,那么lengthAdjustment就是用来修正的。
initialBytesToStrip :8 ,如果在我们业务层只需要 消息体 像消息头 和 数据长度都不需要 那么 initialBytesToStrip就是用来跳过的字节数。
就比如 消息头规定 4个字节 数据长度 4个字节 所以我们如果只要 消息体里的内容 就需要跳过 8个字节 所以设置8 即可
结合发送端和 接收数据端 一个例子 整体
接收数据端的 定义
socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024,4,4,0,8));
发送端
//第四种 自定义解码器//[4个字节 + 4个字节 + 数据]// 我们传输数据 就要这样拼接数据 消息头 +数据长度+数据String message = "Message"+i;//消息头 4个字节ByteBuf byteBuf = Unpooled.buffer();byteBuf.writeInt(102); //【lengthFieldOffset】 4个字节// 102 测试随便写的 只要是占4个字节的数字就行 为啥写个102就4个字节 因为102 是int类型 int类型的字节数是4个字节// 这个应该是 数据类型基础 int string long 等等 占几个字节的问题//数据长度 4个字节 长度也是 int型 所以跟消息头 一样 所以在设置解码器长度参数的时候 【lengthFieldLength】 4个字节就够用byteBuf.writeInt(message.getBytes().length);//发送的数据byteBuf.writeBytes(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8));channel.writeAndFlush(byteBuf);
测试结果
没有解码器的情况
有了之后:
相关文章:

springboot框架使用Netty依赖中解码器的作用及实现详解
在项目开发 有需求 需要跟硬件通信 也没有mqtt 作为桥接 也不能http 请求 api 所以也不能 json字符串这么爽传输 所以要用tcp 请求 进行数据交互 数据还是16进制的 写法 有帧头 什么的 对于这种物联网的这种对接 我的理解就是 我们做的工作就像翻译 把这些看不懂的 字节流 变成…...
Python爬虫实战之爬取京东商品数据
在数字化时代,数据如同黄金般珍贵,而电商数据,尤其是像京东这样的大型电商平台上的信息,更是商家、市场分析师和数据科学家眼中的瑰宝。本文将带您走进Python爬虫的世界,探索如何高效、合法地采集京东商品数据…...

浅析Resource Quota中limits计算机制
前言 在生产环境中,通常需要通过配置资源配额(Resource Quota)来限制一个命名空间(namespace)能使用的资源量。在资源紧张的情况下,常常需要调整工作负载(workload)的请求值…...

《数据结构与算法基础 by王卓老师》学习笔记——1.4算法与算法分析
一、算法 1.1算法的研究内容 1.2算法的定义 1.3算法的描述 以下是算法的自然语言描述 以下是算法的传统流程图表示 以下是NS流程图表示 1.4算法和程序的区别与联系 1.5算法的五个特性 1.6算法设计的要求 Robustness也称为鲁棒性 二、算法分析 2.1算法时间效率的度量 2.1.1事…...
运维团队如何加强安全设备监控与日志管理
随着信息技术的飞速发展,网络安全问题日益凸显,安全设备的监控和日志管理成为了运维团队不可或缺的工作内容。本文将结合运维行业的实际需求,探讨如何加强安全设备监控与日志管理,以提升系统的安全性和稳定性。 一、安全设备监控…...

仓库管理系统13--物资设置
1、添加窗体 2、设计UI界面 注意这个下拉框的绑定,你看到的选项是由displaymember决定,当你选择了哪个选项时,后台绑定这个选项的ID <UserControl x:Class"West.StoreMgr.View.GoodsView"xmlns"http://schemas.microsoft…...

机器人控制系列教程之URDF文件语法介绍
前两期推文:机器人控制系列教程之动力学建模(1)、机器人控制系列教程之动力学建模(2),我们主要从数学的角度介绍了机器人的动力学建模的方式,随着机器人技术的不断发展,机器人建模成为了机器人系统设计中的一项关键任务。URDF&…...

Arathi Basin (AB) PVP15
Arathi Basin (AB) PVP15 阿拉希盆地,PVP,15人战场...

Ubuntu/Linux SSH 端口转发
文章目录 Ubuntu/Linux SSH 端口转发概述本地端口转发场景一场景二 参考资料 Ubuntu/Linux SSH 端口转发 概述 SSH, Secure Shell 是一种在网络上用于安全远程登录到另一台机器的工具。除了远程登录以外,ssh 的端口转发是它的另一项强大功能。通过 ssh 端口转发功…...
flask的locked_cached_property
下面是一个关于 locked_cached_property 装饰器的详细教程。这个装饰器将一个方法转换为一个惰性属性,在第一次访问时计算其值,并在随后的访问中缓存该值。同时,它在多线程环境中是线程安全的。 教程:理解和使用 locked_cached_p…...

OSI七层模型TCP/IP四层面试高频考点
OSI七层模型&TCP/IP四层&面试高频考点 1 OSI七层模型 1. 物理层:透明地传输比特流 在物理媒介上传输原始比特流,定义了连接主机的硬件设备和传输媒介的规范。它确保比特流能够在网络中准确地传输,例如通过以太网、光纤和无线电波等媒…...

Swagger2及常用校验注释说明
Api(value "后台用户管理") RestController RequestMapping("bossuser") public class BossUserController {ApiOperation(value "测试接口")PostMapping("test")public String testUser(Valid RequestBody TestUser user) {LOG.inf…...

【项目实训】各种反爬策略及爬虫困难点总结
在这里,我总结了本次项目的数据收集过程中遇到的反爬虫策略以及一些爬虫过程中容易出现问题的地方。 user-agent 简单的设置user-agent头部为浏览器即可: 爬取标签中带href属性的网页 对于显示岗位列表的页面,通常检查其源代码就会发现&…...

能量智慧流转:全面升级储能电站的智能网关解决方案
监控系统是电化学储能电站的关键组成部分,储能电站也需要相应的监控系统,通过监控系统对储能设备的状态进行监测,实时感知储能设备的健康状态,控制储能设备的充放电功率和时机等, 一个好的监控系统可以实现储能电站安全…...

【金融研究】6月,对冲基金狂卖美国科技股 短期乐观,长期悲观?“油价最大空头”花旗:明年跌到60
科技股新高的背后,是对冲基金与散户投资者的分歧,对冲基金正在向散户投资者出售创纪录数量的科技/半导体/美股“七姐妹”股票。 对冲基金狂卖美国科技股 在五大明星科技股(苹果、亚马逊、微软、英伟达、谷歌)轮番创下历史新高的…...

GroundingDINO1.5突破开放式物体检测界限:介绍与应用
《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…...

centos编译内核ko模块
1、make报错 make: * /lib/modules/4.14.0-49.el7a.aarch64/build: 没有那个文件或目录。 停止。 [rootlocalhost 4.14.0-49.el7a.aarch64]# pwd /lib/modules/4.14.0-49.el7a.aarch64 [rootlocalhost 4.14.0-49.el7a.aarch64]# ll 总用量 1744 lrwxrwxrwx. 1 root root …...

Android13 WMS窗口层级树
1,认识层级树 可以通过dumpsys activity containers 看到 WMS 层级树的结构 ACTIVITY MANAGER CONTAINERS (dumpsys activity containers) ROOT typeundefined modefullscreen override-modeundefined requested-bounds[0,0][0,0] bounds[0,0][1440,2960]#0 Displa…...

计算机毕业设计Python+LSTM+Tensorflow股票分析预测 基金分析预测 股票爬虫 大数据毕业设计 深度学习 机器学习 数据可视化 人工智能
基于TensorFlow-LSTM的股票预测系统开题报告 一、研究背景与意义 随着信息技术的飞速发展,股票市场作为现代经济活动的重要组成部分,其价格波动受到广泛关注。投资者们迫切希望通过科学的方法预测股票价格,以优化投资决策,实现利…...

仓库管理系统14--仓库设置
1、添加窗体 <UserControl x:Class"West.StoreMgr.View.StoreView"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc"http://schemas.openxmlformats.…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...

Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...