基于Netty源代码版本:netty-all-4.1.33.Final
前言
Netty 的解码器有很多种,比如基于长度的,基于分割符的,私有协议的。但是,总体的思路都是一致的。 拆包思路:当数据满足了 解码条件时,将其拆开。放到数组。然后发送到业务 handler 处理。 半包思路: 当读取的数据不够时,先存起来,直到满足解码条件后,放进数组。送到业务 handler 处理。
拆包的原理
在没有netty的情况下,用户如果自己需要拆包,基本原理就是不断从TCP缓冲区中读取数据,每次读取完都需要判断是否是一个完整的数据包
1、如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从tcp缓冲区中读取,直到得到一个完整的数据包
2、如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,够成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接
netty中拆包的基类
netty 中的拆包也是如上这个原理,在每个SocketChannel中会一个 pipeline ,pipeline 内部会加入解码器,解码器都继承基类 ByteToMessageDecoder,其内部会有一个累加器,每次从当前SocketChannel读取到数据都会不断累加,然后尝试对累加到的数据进行拆包,拆成一个完整的业务数据包,下面我们先详细分析下这个类
看名字的意思是:将字节转换成消息的解码器。人如其名。而他本身也是一个入站 handler,所以,我们还是从他的 channelRead 方法入手。
channelRead 方法
我们先看看基类中的属性,cumulation是此基类中的一个 ByteBuf 类型的累积区,每次从当前SocketChannel读取到数据都会不断累加,然后尝试对累加到的数据进行拆包,拆成一个完整的业务数据包,如果不够一个完整的数据包,则等待下一次从TCP的数据到来,继续累加到此cumulation中
public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
ByteBuf cumulation;
private Cumulator cumulator = MERGE_CUMULATOR;
private boolean singleDecode;
private boolean decodeWasNull;
private boolean first;
private byte decodeState = STATE_INIT;
private int discardAfterReads = 16;
private int numReads;
}
channelRead方法是每次从TCP缓冲区读到数据都会调用的方法,触发点在AbstractNioByteChannel的read方法中,里面有个while循环不断读取,读取到一次就触发一次channelRead
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
// 从对象池中取出一个List
CodecOutputList out = CodecOutputList.newInstance();
try {
ByteBuf data = (ByteBuf) msg;
first = cumulation == null;
if (first) {
// 第一次解码
cumulation = data;//直接赋值
} else {
// 第二次解码,就将 data 向 cumulation 追加,并释放 data
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
}
// 得到追加后的 cumulation 后,调用 decode 方法进行解码
// 主要目的是将累积区cumulation的内容 decode 到 out数组中
callDecode(ctx, cumulation, out);
} catch (DecoderException e) {
throw e;
} catch (Exception e) {
throw new DecoderException(e);
} finally {
// 如果累计区没有可读字节了,有可能在上面callDecode方法中已经将cumulation全部读完了,此时writerIndex==readerIndex
// 每读一个字节,readerIndex会+1
if (cumulation != null && !cumulation.isReadable()) {
// 将次数归零
numReads = 0;
// 释放累计区,因为累计区里面的字节都全部读完了
cumulation.release();
// 便于 gc
cumulation = null;
// 如果超过了 16 次,还有字节没有读完,就将已经读过的数据丢弃,将 readIndex 归零。
} else if (++ numReads >= discardAfterReads) {
// We did enough reads already try to discard some bytes so we not risk to see a OOME.
// See https://github.com/netty/netty/issues/4275
numReads = 0;
//将已经读过的数据丢弃,将 readIndex 归零。
discardSomeReadBytes();
}
int size = out.size();
decodeWasNull = !out.insertSinceRecycled();
//循环数组,向后面的 handler 发送数据
fireChannelRead(ctx, out, size);
out.recycle();
}
} else {
ctx.fireChannelRead(msg);
}
}
1、从对象池中取出一个空的数组。
2、判断成员变量是否是第一次使用,将 unsafe 中传递来的数据写入到这个 cumulation 累积区中。
3、写到累积区后,在callDecode方法中调用子类的 decode 方法,尝试将累积区的内容解码,每成功解码一个,就调用后面节点的 channelRead 方法。若没有解码成功,什么都不做。
4、如果累积区没有未读数据了,就释放累积区。
5、如果还有未读数据,且解码超过了 16 次(默认),就对累积区进行压缩。将读取过的数据清空,也就是将 readIndex 设置为0.
6、调用 fireChannelRead 方法,将数组中的元素发送到后面的 handler 中。
7、将数组清空。并还给对象池。
下面来说说详细的步骤。
写入累积区
如果当前累加器没有数据,就直接跳过内存拷贝,直接将字节容器的指针指向新读取的数据,否则,调用累加器累加数据至字节容器
ByteBuf data = (ByteBuf) msg;
first = cumulation == null;
if (first) {
// 第一次解码
cumulation = data;//直接赋值
} else {
// 第二次解码,就将 data 向 cumulation 追加,并释放 data
cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
}
然后看到ByteToMessageDecoder的成员变量private Cumulator cumulator = MERGE_CUMULATOR;接下来看看 MERGE_CUMULATOR
public static final Cumulator MERGE_CUMULATOR = new Cumulator() {
@Override
public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
try {
final ByteBuf buffer;
if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes()
|| cumulation.refCnt() > 1 || cumulation.isReadOnly()) {
// Expand cumulation (by replace it) when either there is not more room in the buffer
// or if the refCnt is greater then 1 which may happen when the user use slice().retain() or
// duplicate().retain() or if its read-only.
//
// See:
// - https://github.com/netty/netty/issues/2327
// - https://github.com/netty/netty/issues/1764
buffer = expandCumulation(alloc, cumulation, in.readableBytes());
} else {
buffer = cumulation;
}
buffer.writeBytes(in);
return buffer;
} finally {
// We must release in in all cases as otherwise it may produce a leak if writeBytes(...) throw
// for whatever release (for example because of OutOfMemoryError)
in.release();
}
}
};
MERGE_CUMULATOR是基类ByteToMessageDecoder中的一个静态常量,其重写了cumulate方法,下面我们看一下 MERGE_CUMULATOR 是如何将新读取到的数据累加到字节容器里的
netty 中ByteBuf的抽象,使得累加非常简单,通过一个简单的api调用 buffer.writeBytes(in); 便将新数据累加到字节容器中,为了防止字节容器大小不够,在累加之前还进行了扩容处理
static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) {
ByteBuf oldCumulation = cumulation;
cumulation = alloc.buffer(oldCumulation.readableBytes() + readable);
cumulation.writeBytes(oldCumulation);
oldCumulation.release();
return cumulation;
}
扩容也是一个内存拷贝操作,新增的大小即是新读取数据的大小
将累加到的数据传递给业务进行拆包 当数据追加到累积区之后,需要调用 decode 方法进行解码,代码如下:
public abstract class AbstractByteBuf extends ByteBuf {
@Override
public boolean isReadable() {
//写的坐标大于读的坐标则说明还有数据可读
return writerIndex > readerIndex;
}
}
public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List out) {
try {
// 如果累计区还有可读字节,循环解码,因为这里in有可能是粘包,即多次完整的数据包粘在一起,通过换行符连接
// 下面的decode方法只能处理一个完整的数据包,所以这里循环处理粘包
while (in.isReadable()) {
int outSize = out.size();
// 上次循环成功解码
if (outSize > 0) {
// 处理一个粘包就 调用一次后面的业务 handler 的 ChannelRead 方法
fireChannelRead(ctx, out, outSize);
// 将 size 置为0
out.clear();
// Check if this handler was removed before continuing with decoding.
// If it was removed, it is not safe to continue to operate on the buffer.
//
// See:
// - https://github.com/netty/netty/issues/4635
if (ctx.isRemoved()) {
break;
}
outSize = 0;
}
// 得到可读字节数
int oldInputLength = in.readableBytes();
// 调用 decode 方法,将成功解码后的数据放入道 out 数组中
decodeRemovalReentryProtection(ctx, in, out);
// Check if this handler was removed before continuing the loop.
// If it was removed, it is not safe to continue to operate on the buffer.
//
// See https://github.com/netty/netty/issues/1664
if (ctx.isRemoved()) {
break;
}
if (outSize == out.size()) {
if (oldInputLength == in.readableBytes()) {
break;
} else {
continue;
}
}
if (oldInputLength == in.readableBytes()) {
throw new DecoderException(
StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
}
if (isSingleDecode()) {
break;
}
}
} catch (DecoderException e) {
throw e;
} catch (Exception cause) {
throw new DecoderException(cause);
}
}
final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List out)
throws Exception {
decodeState = STATE_CALLING_CHILD_DECODE;
try {
decode(ctx, in, out);
} finally {
boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
decodeState = STATE_INIT;
if (removePending) {
handlerRemoved(ctx);
}
}
}
}
我们看看 fireChannelRead
static void fireChannelRead(ChannelHandlerContext ctx, List msgs, int numElements) {
if (msgs instanceof CodecOutputList) {
fireChannelRead(ctx, (CodecOutputList) msgs, numElements);
} else {
//将所有已解码的数据向下业务hadder传递
for (int i = 0; i < numElements; i++) {
ctx.fireChannelRead(msgs.get(i));
}
}
}
该方法主要逻辑:只要累积区还有未读数据,就循环进行读取。
1、调用 decodeRemovalReentryProtection 方法,内部调用了子类重写的 decode 方法,很明显,这里是个模板模式。decode 方法的逻辑就是将累积区的内容按照约定进行解码,如果成功解码,就添加到数组中。同时该方法也会检查该 handler 的状态,如果被移除出 pipeline 了,就将累积区的内容直接刷新到后面的 handler 中。
2、如果 Context 节点被移除了,直接结束循环。如果解码前的数组大小和解码后的数组大小相等,且累积区的可读字节数没有变化,说明此次读取什么都没做,就直接结束。如果字节数变化了,说明虽然数组没有增加,但确实在读取字节,就再继续读取。
3、如果上面的判断过了,说明数组读到数据了,但如果累积区的 readIndex 没有变化,则抛出异常,说明没有读取数据,但数组却增加了,子类的操作是不对的。
4、如果是个单次解码器,解码一次就直接结束了,如果数据包一次就解码完了,则下一次循环时 in.isReadable()就为false,因为 writerIndex = this.readerIndex 了
所以,这段代码的关键就是子类需要重写 decode 方法,将累积区的数据正确的解码并添加到数组中。每添加一次成功,就会调用 fireChannelRead 方法,将数组中的数据传递给后面的 handler。完成之后将数组的 size 设置为 0.
所以,如果你的业务 handler 在这个地方可能会被多次调用。也可能一次也不调用。取决于数组中的值。
解码器最主要的逻辑: 将 read 方法的数据读取到累积区,使用解码器解码累积区的数据,解码成功一个就放入到一个数组中,并将数组中的数据一次次的传递到后面的handler。
清理字节容器
业务拆包完成之后,只是从累积区中取走了数据,但是这部分空间对于累积区来说依然保留着,而字节容器每次累加字节数据的时候都是将字节数据追加到尾部,如果不对累积区做清理,那么时间一长就会OOM,清理部分的代码如下:
finally {
// 如果累计区没有可读字节了,有可能在上面callDecode方法中已经将cumulation全部读完了,此时writerIndex==readerIndex
// 每读一个字节,readerIndex会+1
if (cumulation != null && !cumulation.isReadable()) {
// 将次数归零
numReads = 0;
// 释放累计区,因为累计区里面的字节都全部读完了
cumulation.release();
// 便于 gc
cumulation = null;
// 如果超过了 16 次,还有字节没有读完,就将已经读过的数据丢弃,将 readIndex 归零。
} else if (++ numReads >= discardAfterReads) {
// We did enough reads already try to discard some bytes so we not risk to see a OOME.
// See https://github.com/netty/netty/issues/4275
numReads = 0;
//将已经读过的数据丢弃,将 readIndex 归零。
discardSomeReadBytes();
}
int size = out.size();
decodeWasNull = !out.insertSinceRecycled();
//循环数组,向后面的 handler 发送数据
fireChannelRead(ctx, out, size);
out.recycle();
}
public abstract class AbstractByteBuf extends ByteBuf {
@Override
public ByteBuf discardSomeReadBytes() {
ensureAccessible();
if (readerIndex == 0) {
return this;
}
if (readerIndex == writerIndex) {
adjustMarkers(readerIndex);
writerIndex = readerIndex = 0;
return this;
}
//读指针超过了Buffer容量的一半时做清理工作
if (readerIndex >= capacity() >>> 1) {
//拷贝,从readerIndex开始,拷贝this.writerIndex - this.readerIndex 长度
setBytes(0, this, readerIndex, writerIndex - readerIndex);
writerIndex -= readerIndex;
adjustMarkers(readerIndex);
//将读指针重置为0
readerIndex = 0;
}
return this;
}
}
我们看到discardSomeReadBytes 主要是将未读的数据拷贝到原Buffer,重置 readerIndex 和 writerIndex
我们看到最后还调用 fireChannelRead 方法,尝试将数组中的数据发送到后面的 handler。为什么要这么做。按道理,到这一步的时候,数组不可能是空,为什么这里还要这么谨慎的再发送一次?
如果是单次解码器,就需要发送了,因为单词解码器是不会在 callDecode 方法中发送的。
总结
可以说,ByteToMessageDecoder 是解码器的核心所做,Netty 在这里使用了模板模式,留给子类扩展的方法就是 decode 方法。
主要逻辑就是将所有的数据全部放入累积区,子类从累积区取出数据进行解码后放入到一个 数组中,ByteToMessageDecoder 会循环数组调用后面的 handler 方法,将数据一帧帧的发送到业务 handler 。完成这个的解码逻辑。
使用这种方式,无论是粘包还是拆包,都可以完美的实现。
Netty 所有的解码器,都可以在此类上扩展,一切取决于 decode 的实现。只要遵守 ByteToMessageDecoder 的约定即可。
Netty中内置了几个编解码器,可以很简单的处理包界限问题。
LengthFieldBasedFrameDecoder 通过在包头增加消息体长度的解码器,解析数据时首先获取首部长度,然后定长读取socket中的数据。
LineBasedFrameDecoder 换行符解码器,报文尾部增加固定换行符rn,解析数据时以换行符作为报文结尾。
DelimiterBasedFrameDecoder 分隔符解码器,使用特定分隔符作为报文的结尾,解析数据时以定义的分隔符作为报文结尾
FixedLengthFrameDecoder 定长解码器,这个最简单,消息体固定长度,解析数据时按长度读取即可
参考: https://www.cnblogs.com/java-chen-hao/p/11436512.html
https://www.cnblogs.com/jpfss/p/11497354.html
https://segmentfault.com/a/1190000020238448
你可能感兴趣的:(Netty源码分析——拆包器的奥秘)
Vue3中的ref与reactive:构建响应式数据的双刃剑
明日筑梦师
vue vue.js ubuntu 前端
一、使用方式1.ref的使用import{ref}from'vue';//创建一个响应式的计数器constcount=ref(0);//修改值count.value++;//增加计数//在模板中直接绑定Countis:{{count.value}}2.reactive的使用import{reactive}from'vue';//创建一个响应式对象constuser=reactive({name:'
React 前端框架搭建与解析
咚微灯
前端框架
React前端框架搭建与解析一、概述React是Facebook开源的用于构建用户界面的JavaScript库,以其组件化、声明式编程范式以及高效的虚拟DOM渲染机制,成为当今最流行的前端框架之一。本文将带领你从零开始搭建一个React开发环境,并深入解析其核心概念。二、环境搭建1.安装Node.js和npmReact依赖于Node.js和npm(Node.js包管理器)。前往Node.js官网下
详细说说VIT架构和Transformer架构的异同
AI生成曾小健
大模型LLM面试指南 多模态MLLM大模型面试指南 架构 transformer 深度学习
GPT-4oVisionTransformer(ViT)和Transformer架构之间的关系非常紧密,因为ViT是直接将Transformer应用到视觉任务中的一种方法。不过,由于图像数据与自然语言数据的特性不同,ViT在实现上对标准Transformer架构做了一些调整。以下是ViT和Transformer架构的异同点详细分析:1.Transformer架构的回顾Transformer是一种用
vue REF 和 Reactive区别、特点、优势
摸鱼的张三
vue vue3 vue.js 前端 javascript
REF和Reactive是两种不同的编程范式。下面是它们之间的对比以及各自的优势劣势和特点:REF(可变状态编程):优势:易于理解和学习:REF编程模型更贴近传统的命令式编程,因此对于大多数开发者来说更容易理解和学习。简单易用:REF状态是可变的,可以直接对其进行操作和修改,使得编写代码更加直观和简单。性能较高:由于REF的状态是可变的,可以直接对其进行修改,因此在一些性能敏感的场景下可能会比Re
计算机网络之局域网(局域网的基本概念和体系结构)
DKPT
# 计算机网络 计算机网络 算法 学习 笔记 开发语言
一、基本概念地理范围:局域网通常覆盖较小的地理范围,如一座建筑物、一个校园或一个公司内。连接设备:它通过传输介质(如有线的双绞线、同轴电缆或无线的Wi-Fi)将分散在该区域内的多台计算机及其外围设备连接起来。数据传输:局域网内的设备可以实现高速数据传输,速率通常在几百Mbps到几Gbps之间。资源共享:局域网允许连接的设备之间进行数据通讯和资源共享,如打印机、存储设备等。私有网络:局域网通常是某个
Python基于 Flask 创建简单Web服务并接收文件
IOsetting
Python flask
在全部网口上创建web服务,监听8080端口关闭debug模式GET时返回HTML界面,用于提交文件POST到/upload时,从接收的file变量中读取文件,并传递给opencv解析为image对象fromflaskimportFlask,request,redirect,url_forimportosimportcv2importnumpyimportjsonapp=Flask(__name_
学习ubuntu 24.10系统目录架构
owerm
学习 ubuntu linux
Ubuntu系统的目录架构是一种标准化的文件系统布局,它按照特定的目录结构组织文件和目录,使得系统管理和维护更加简便。以下是Ubuntu系统目录的用途简单介绍:1、/bin:全名是"binary",二进制1-1、用途:存放目录包含二进制可执行文件,包含系统中最基本的可执行命令,如ls、cp和mv等。这些命令对所有用户可用,是系统运行所必需。1-2、Ubuntu24.10版本中,/bin目录是一个符
软考中级 软件设计师 第二章 第六节 磁盘管理
烟锁迷城
笔记
目录1、磁盘读取时间2、移臂调度算法3、关于缓冲区和时间3.1、单缓冲区3.2、双缓冲区4、I/O管理软件1、磁盘读取时间每一个磁盘都是由磁道和扇区组成,最外一层被称为0磁道。想要寻找到某个数据,就要从磁道开始寻找,扇区会自己旋转,这样一定能找到自己要找的数据,只是需要进行等待,因此,存取时间的计算公式为:存取时间=寻道时间+等待时间寻道时间:磁头移动到磁道所需的时间。等待时间:等待读写的扇区转到
Golang学习笔记_29——抽象工厂模式
LuckyLay
golang 学习 笔记 抽象工厂模式
Golang学习笔记_26——通道Golang学习笔记_27——单例模式Golang学习笔记_28——工厂方法模式文章目录抽象工厂模式一、抽象工厂模式核心概念1.解决的问题2.关键角色3.类图二、模式特点三、适用场景1.需要创建一组相关或依赖的对象2.系统需要独立于产品的创建、组合和表示3.需要确保产品之间的兼容性4.需要动态切换产品族5.需要隐藏具体产品的实现细节四、Go语言代码示例五、与其他模
六西格玛设计DFSS:让企业运营零浪费高收益——张驰咨询
张驰课堂
六西格玛设计 DFSS
在当今全球化和数字化的商业环境中,企业面临着前所未有的挑战与机遇。为了在激烈的市场竞争中脱颖而出,企业不仅需要创新的产品和服务,更需要高效、精准的管理体系。六西格玛设计(DesignforSixSigma,DFSS),作为一种以数据驱动、顾客为中心的管理哲学,正成为企业实现精益管理、提升竞争力的关键工具。六西格玛设计DFSS的核心理念六西格玛设计不仅仅是一种方法论,更是一种思维方式。它强调在产品设
六西格玛培训:解锁红海迷局,打造企业蓝海战略——张驰咨询
张驰课堂
六西格玛培训
在过去的二十年中,六西格玛管理被国内众多企业视为降本增效、培养人才、提升经营效益的高效工具。然而,随着经济环境的不断变化,行业内卷加剧,许多企业即便已经实施了六西格玛管理,依然面临业绩下滑的困扰。为何这些管理变革手段对企业经营状况的改善帮助有限?企业又该如何通过六西格玛管理在激烈的市场竞争中创造出属于自己的蓝海?本文张驰咨询将详细探讨这些问题,旨在为当前陷入困境的企业提供一条可行的突围之路。为什么
精益六西格玛入门:从理论到实践的全面解析——张驰咨询
张驰课堂
精益六西格玛
在质量管理领域,六西格玛与精益管理是两个常被提及且相辅相成的概念。对于初次接触六西格玛的质量人来说,如何学习、学到何种程度才算入门,以及是否应该同时学习精益六西格玛,这些问题无疑会成为他们探索之路上的重要疑问。今天,张驰咨询就来一一解答这些疑惑,为您的六西格玛学习之旅指明方向。一、六西格玛与精益六西格玛:选择哪条路?在质量管理领域,六西格玛与精益方法各有其独特的魅力与实用性。六西格玛方法的核心在于
Chrome内核解析 -- 背景篇:Chromium的多进程多线程构架
yunchao_he
Chromium/Blink Rendering Chromium multi-process
转载请注明出处:http://blog.csdn.net/yunchao_he/article/details/41695497Chromium采用多进程构架,以DesktopChromium为例,它包括一个BrowserProcess(也称为UIProcess),一个或多个RenderProcess(也称为WebProcess),零个或一个GPUProcess,以及一个或多个NPAPIPlugi
无人机电机故障率骤降:创新设计与六西格玛方法论双赢
张驰课堂
无人机 六西格玛
项目背景TBR-100是消费级无人机头部企业推出的主打消费级无人机,凭借其出色的续航能力和卓越的操控性,在市场上获得了广泛认可。在产品运行过程,用户反馈电机故障率偏高,尤其是在飞行一段时间后出现电机过热、损坏以及运行不稳定等问题。这不仅影响了产品的使用寿命,也增加了维修和售后成本,对公司品牌声誉带来了负面影响。因此,该公司与张驰咨询合作,启动了一个六西格玛黑带项目,结合DMAIC方法论和第一性原理
SpringCloud包含的微服务介绍--Eureka
wildyuhao
Java 网络 zookeeper 分布式 eureka spring
SpringCloud包含的微服务介绍Eureka服务注册与发现为什么需要注册中心当我们启动项目时,我们通常会在属性文件中包含所有配置。随着越来越多的服务的开发和部署,添加和修改这些属性变得更加复杂。某些服务可能会停止运行,而某些服务可能会发生变化。手动更改属性可能会产生问题。Eureka服务注册和发现在这种情况下有所帮助。由于所有服务都已注册到Eureka服务器并通过调用EurekaServer
Python----PyQt开发(PyQt高级:文件浏览器)
蹦蹦跳跳真可爱589
Python PyQt pyqt python
一、效果展示二、界面设计该界面通过QtDesigner设计#-*-coding:utf-8-*-#Formimplementationgeneratedfromreadinguifile'file_web.ui'##Createdby:PyQt5UIcodegenerator5.15.9##WARNING:Anymanualchangesmadetothisfilewillbelostwhenpy
Linux操作系统:基础与文件系统结构研究
暮雨哀尘
Linux的那点事 linux 网络 网络安全 嵌入式 服务器 文件系统 权限
Linux系统基础与文件系统结构研究摘要本文深入探讨了Linux操作系统的常用命令、文件系统结构及其层次标准(FHS),并结合实际应用场景,详细分析了文件和目录操作、文件查看、系统信息查看以及用户和权限管理等核心功能。通过对Linux文件系统层次结构标准的深入剖析,阐述了根目录及其子目录的作用,并通过实验验证了常用命令的实际应用效果。本文旨在为Linux初学者提供一个全面的入门指南,同时也为有一定
常用的数据结构的时间复杂度
跟着杰哥学嵌入式
数据结构
下面是常用数据结构及其常见操作(如插入、删除、查找等)时间复杂度的表格。表格中列出了每种数据结构的常见操作在不同情况下的时间复杂度。数据结构操作平均时间复杂度最坏时间复杂度最优时间复杂度数组插入/删除O(n)O(n)O(1)查找O(1)O(1)O(1)更新O(1)O(1)O(1)链表插入/删除O(1)O(1)O(1)查找O(n)O(n)O(n)更新O(n)O(n)O(n)栈插入/删除O(1)O(1
洞察瑞芯微 RK3576 边缘工控机的丰富接口扩展
钡铼技术物联网关
python 边缘计算
在工业控制领域,高效的数据采集和设备连接是实现智能化生产的关键。瑞芯微RK3576边缘工控机以其出色的多接口IO和串口等扩展能力,为工业应用提供了强大的支持。RK3576边缘工控机的多接口IO设计使其能够轻松连接各类传感器、执行器和外部设备。无论是数字输入输出接口,还是模拟输入输出接口,都能满足不同工业场景的需求。串口扩展功能更是为与传统工业设备的通信提供了便利。在一些老旧的工业系统中,串口通信仍
国产ARM处理器工控机如何助力企业实现自主可控?
钡铼技术物联网关
人工智能 大数据 物联网 边缘计算 linux
选择国产ARM处理器工控机的原因可以从多个角度来考虑,包括技术、经济、安全和政策等方面。以下是一些关键理由:技术优势低功耗高效能:ARM架构以其出色的能效比著称,适合需要长时间运行的工业控制应用。适应性强:国产ARM处理器工控机通常设计为能够在恶劣环境下稳定工作,比如宽温操作、防尘防水等特性。经济考量成本效益:国产产品往往能够提供更具竞争力的价格,降低了企业的采购成本。本地支持:更容易获得本地化的
教程 | Proxmox VE(PVE)安装全流程指南(末尾附镜像及快速配置脚本)
The god of big data
虚拟系统 神器?三叉戟? windows 开发语言 容器
ProxmoxVE是一款基于Debian的开源虚拟化平台,支持KVM虚拟机和LXC容器,广泛用于企业级虚拟化部署。一、安装前准备1.硬件要求CPU:64位处理器(IntelVT/AMD-V虚拟化支持)内存:至少4GB(推荐8GB以上)存储:50GB可用空间(SSD更佳)网络:千兆网卡2.下载镜像访问Proxmox官网获取最新ISO文件(当前稳定版为8.x)。3.制作启动盘使用工具(如Rufus、B
RK3588NVR 没有声音输出有检测到声卡
bug菌¹
全栈Bug调优(实战版) 边缘计算 RK3588NVR andorid14
本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!全文目录:问题描述解决方案1.**检查Android音频系统设置**2.**ES8311驱动加载问题**3.**检查音频输出的其他部分**4.**HDMI音频输出问题**
计算机视觉四大任务模型汇总
Zero_one_ws
《神经网络与深度学习》理论 计算机视觉 人工智能 深度学习 图像分类 图像目标检测 目标分割 关键点检测
计算机视觉中有四大核心任务:1-分类任务、2-目标检测任务、3-目标分割任务和4-关键点检测任务文章1:一文读懂计算机视觉4大任务文章2:图像的目标分割任务:语义分割和实例分割不同任务之间相关但不完全相同,因此不同的任务最好选择相应的模型,话不多说,看表:(注:表中github链接并不一定是模型的正式版本,只是本文用于展示模型的网络结构和应用)1-分类任务模型序号模型ipynb模型的github链
计算机视觉(Computer Vision,CV)四大基本任务--分类、检测、定位、分割
明月光舞
计算机视觉 计算机视觉 目标检测 深度学习
文章目录前言一、计算机视觉任务一:目标分类常用数据集常见网络结构二、计算机视觉任务二:目标定位三、计算机视觉任务三:目标检测常用数据集常见网络结构四、计算机视觉任务四:目标分割常用数据集常见网络结构前言计算机视觉(ComputerVision,CV)是一门研究如何让机器具备“看”的能力的学科,以人或动物的视觉能力为参照,通过计算机对视觉数据(图像、视频等)的处理、学习、推理判断,复现出、模拟出甚至
重学Java设计模式-责任链模式
YWang665
设计模式 java 设计模式 责任链模式
简介在软件开发中,设计模式是解决特定问题的成熟模板,它们提供了一种标准的方式来解决常见的软件设计问题。责任链模式是一种行为设计模式,允许你将请求沿着处理者链进行发送。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。1.定义与基本概念定义责任链模式是一种对象行为型模式,它包含多个对象,每个对象都包含对下一个对象的引用,构成一条链。请求在这个链上传递,每个对象都可以选择处理请求
使用 OpenAI API 创建智能聊天机器人
vaidfl
机器人 python
1.技术背景介绍在人工智能应用中,聊天机器人是一种非常流行的应用。得益于近几年自然语言处理(NLP)技术的飞速发展,聊天机器人已经从简单的问答模式发展到能够进行复杂对话的智能助手。本篇文章将深入介绍如何使用OpenAI提供的API构建一个智能聊天机器人,并通过实际代码演示实现过程。2.核心原理解析OpenAI提供的GPT模型是目前最先进的语言生成模型之一,它可以生成自然流畅的文本。我们可以通过调用
AI赋能:构建你的个性化前端开发学习路径
前端
在竞争激烈的程序员职业发展道路上,持续学习和提升技能至关重要。尤其对于前端开发者而言,技术的日新月异要求我们不断适应新的框架、工具和理念。而个性化学习路径,则成为提升学习效率,快速掌握新技能的关键。今天,我们将探讨如何利用AI代码生成器等AI工具,构建一条高效的前端开发学习路径,助力你快速提升技能,在职业发展中脱颖而出。AI如何革新前端开发学习方式传统的学习方式往往是枯燥的教程和大量的练习,学习曲
AI人工智能深度学习算法:在缺陷检测中的应用
AI天才研究院
DeepSeek R1 & 大数据AI人工智能大模型 AI大模型企业级应用开发实战 计算 计算科学 神经计算 深度学习 神经网络 大数据 人工智能 大型语言模型 AI AGI LLM Java Python 架构设计 Agent RPA
AI人工智能深度学习算法:在缺陷检测中的应用1.背景介绍1.1缺陷检测的重要性在制造业中,产品质量是关键因素之一。缺陷检测是确保产品质量的重要环节,旨在及时发现并排除产品中的任何缺陷或异常。传统的人工目视检测方法不仅效率低下,而且容易出现疲劳导致的错误。因此,开发高效、准确的自动化缺陷检测系统已成为当务之急。1.2人工智能在缺陷检测中的作用随着深度学习技术的不断发展,人工智能(AI)已成为解决缺陷
写给开发者的软件架构实战:微服务架构的实施与优化
AI天才研究院
架构师必知必会系列 大数据 人工智能 语言模型 Java Python 架构设计
作者:禅与计算机程序设计艺术1.背景介绍什么是微服务架构?微服务架构又称SOA(Service-OrientedArchitecture),它是一种分布式架构模式,将单体应用转变成一组小型服务。从本质上看,它将复杂应用程序拆分成多个独立部署、自治的服务单元,每个服务单元都可以独立地运行、升级和扩展。因此,它使得开发人员更容易维护应用程序。微服务架构在某些情况下也比单体架构更具优势。例如,在复杂性较
ALBERT:轻量级的BERT,用于语言表征的自监督学习
人工智能
ALBERT:轻量级的BERT,用于语言表征的自监督学习阅读时长:19分钟发布时间:2025-02-13近日热文:全网最全的神经网络数学原理(代码和公式)直观解释欢迎关注知乎和公众号的专栏内容LLM架构专栏知乎LLM专栏知乎【柏企】公众号【柏企科技说】【柏企阅文】ALBERT提出了特定的参数缩减技术,以降低内存消耗并提高BERT的训练速度。模型架构选择ALBERT架构的主干与BERT类似,它使用带
关于旗正规则引擎规则中的上传和下载问题
何必如此
文件下载 压缩 jsp 文件上传
文件的上传下载都是数据流的输入输出,大致流程都是一样的。
一、文件打包下载
1.文件写入压缩包
string mainPath="D:\upload\"; 下载路径
string tmpfileName=jar.zip; &n
【Spark九十九】Spark Streaming的batch interval时间内的数据流转源码分析
bit1129
Stream
以如下代码为例(SocketInputDStream):
Spark Streaming从Socket读取数据的代码是在SocketReceiver的receive方法中,撇开异常情况不谈(Receiver有重连机制,restart方法,默认情况下在Receiver挂了之后,间隔两秒钟重新建立Socket连接),读取到的数据通过调用store(textRead)方法进行存储。数据
spark master web ui 端口8080被占用解决方法
daizj
8080 端口占用 spark master web ui
spark master web ui 默认端口为8080,当系统有其它程序也在使用该接口时,启动master时也不会报错,spark自己会改用其它端口,自动端口号加1,但为了可以控制到指定的端口,我们可以自行设置,修改方法:
1、cd SPARK_HOME/sbin
2、vi start-master.sh
3、定位到下面部分
oracle_执行计划_谓词信息和数据获取
周凡杨
oracle 执行计划
oracle_执行计划_谓词信息和数据获取(上)
一:简要说明
在查看执行计划的信息中,经常会看到两个谓词filter和access,它们的区别是什么,理解了这两个词对我们解读Oracle的执行计划信息会有所帮助。
简单说,执行计划如果显示是access,就表示这个谓词条件的值将会影响数据的访问路径(表还是索引),而filter表示谓词条件的值并不会影响数据访问路径,只起到
spring中datasource配置
g21121
dataSource
datasource配置有很多种,我介绍的一种是采用c3p0的,它的百科地址是:
http://baike.baidu.com/view/920062.htm
<!-- spring加载资源文件 -->
<bean name="propertiesConfig"
class="org.springframework.b
web报表工具FineReport使用中遇到的常见报错及解决办法(三)
老A不折腾
finereport FAQ 报表软件
这里写点抛砖引玉,希望大家能把自己整理的问题及解决方法晾出来,Mark一下,利人利己。
出现问题先搜一下文档上有没有,再看看度娘有没有,再看看论坛有没有。有报错要看日志。下面简单罗列下常见的问题,大多文档上都有提到的。
1、repeated column width is largerthan paper width:
这个看这段话应该是很好理解的。比如做的模板页面宽度只能放
mysql 用户管理
墙头上一根草
linux mysql user
1.新建用户 //登录MYSQL@>mysql -u root -p@>密码//创建用户mysql> insert into mysql.user(Host,User,Password) values(‘localhost’,'jeecn’,password(‘jeecn’));//刷新系统权限表mysql>flush privileges;这样就创建了一个名为:
关于使用Spring导致c3p0数据库死锁问题
aijuans
spring Spring 入门 Spring 实例 Spring3 Spring 教程
这个问题我实在是为整个 springsource 的员工蒙羞
如果大家使用 spring 控制事务,使用 Open Session In View 模式,
com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.
百度词库联想
annan211
百度
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>RunJS</title&g
int数据与byte之间的相互转换实现代码
百合不是茶
位移 int转byte byte转int 基本数据类型的实现
在BMP文件和文件压缩时需要用到的int与byte转换,现将理解的贴出来;
主要是要理解;位移等概念 http://baihe747.iteye.com/blog/2078029
int转byte;
byte转int;
/**
* 字节转成int,int转成字节
* @author Administrator
*
简单模拟实现数据库连接池
bijian1013
java thread java多线程 简单模拟实现数据库连接池
简单模拟实现数据库连接池
实例1:
package com.bijian.thread;
public class DB {
//private static final int MAX_COUNT = 10;
private static final DB instance = new DB();
private int count = 0;
private i
一种基于Weblogic容器的鉴权设计
bijian1013
java weblogic
服务器对请求的鉴权可以在请求头中加Authorization之类的key,将用户名、密码保存到此key对应的value中,当然对于用户名、密码这种高机密的信息,应该对其进行加砂加密等,最简单的方法如下:
String vuser_id = "weblogic";
String vuse
【RPC框架Hessian二】Hessian 对象序列化和反序列化
bit1129
hessian
任何一个对象从一个JVM传输到另一个JVM,都要经过序列化为二进制数据(或者字符串等其他格式,比如JSON),然后在反序列化为Java对象,这最后都是通过二进制的数据在不同的JVM之间传输(一般是通过Socket和二进制的数据传输),本文定义一个比较符合工作中。
1. 定义三个POJO
Person类
package com.tom.hes
【Hadoop十四】Hadoop提供的脚本的功能
bit1129
hadoop
1. hadoop-daemon.sh
1.1 启动HDFS
./hadoop-daemon.sh start namenode
./hadoop-daemon.sh start datanode
通过这种逐步启动的方式,比start-all.sh方式少了一个SecondaryNameNode进程,这不影响Hadoop的使用,其实在 Hadoop2.0中,SecondaryNa
中国互联网走在“灰度”上
ronin47
管理 灰度
中国互联网走在“灰度”上(转)
文/孕峰
第一次听说灰度这个词,是任正非说新型管理者所需要的素质。第二次听说是来自马化腾。似乎其他人包括马云也用不同的语言说过类似的意思。
灰度这个词所包含的意义和视野是广远的。要理解这个词,可能同样要用“灰度”的心态。灰度的反面,是规规矩矩,清清楚楚,泾渭分明,严谨条理,是决不妥协,不转弯,认死理。黑白分明不是灰度,像彩虹那样
java-51-输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
bylijinnan
java
public class PrintMatrixClockwisely {
/**
* Q51.输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
例如:如果输入如下矩阵:
1 2 3 4
5 6 7 8
9
mongoDB 用户管理
开窍的石头
mongoDB用户管理
1:添加用户
第一次设置用户需要进入admin数据库下设置超级用户(use admin)
db.addUsr({user:'useName',pwd:'111111',roles:[readWrite,dbAdmin]});
第一个参数用户的名字
第二个参数
[游戏与生活]玩暗黑破坏神3的一些问题
comsci
生活
暗黑破坏神3是有史以来最让人激动的游戏。。。。但是有几个问题需要我们注意
玩这个游戏的时间,每天不要超过一个小时,且每次玩游戏最好在白天
结束游戏之后,最好在太阳下面来晒一下身上的暗黑气息,让自己恢复人的生气
&nb
java 二维数组如何存入数据库
cuiyadll
java
using System;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace WindowsFormsApplication1
{
本地事务和全局事务Local Transaction and Global Transaction(JTA)
darrenzhu
java spring local global transaction
Configuring Spring and JTA without full Java EE
http://spring.io/blog/2011/08/15/configuring-spring-and-jta-without-full-java-ee/
Spring doc -Transaction Management
http://docs.spring.io/spri
Linux命令之alias - 设置命令的别名,让 Linux 命令更简练
dcj3sjt126com
linux alias
用途说明
设置命令的别名。在linux系统中如果命令太长又不符合用户的习惯,那么我们可以为它指定一个别名。虽然可以为命令建立“链接”解决长文件名的问 题,但对于带命令行参数的命令,链接就无能为力了。而指定别名则可以解决此类所有问题【1】。常用别名来简化ssh登录【见示例三】,使长命令变短,使常 用的长命令行变短,强制执行命令时询问等。
常用参数
格式:alias
格式:ali
yii2 restful web服务[格式响应]
dcj3sjt126com
PHP yii2
响应格式
当处理一个 RESTful API 请求时, 一个应用程序通常需要如下步骤 来处理响应格式:
确定可能影响响应格式的各种因素, 例如媒介类型, 语言, 版本, 等等。 这个过程也被称为 content negotiation。
资源对象转换为数组, 如在 Resources 部分中所描述的。 通过 [[yii\rest\Serializer]]
MongoDB索引调优(2)——[十]
eksliang
mongodb MongoDB索引优化
转载请出自出处:http://eksliang.iteye.com/blog/2178555 一、概述
上一篇文档中也说明了,MongoDB的索引几乎与关系型数据库的索引一模一样,优化关系型数据库的技巧通用适合MongoDB,所有这里只讲MongoDB需要注意的地方 二、索引内嵌文档
可以在嵌套文档的键上建立索引,方式与正常
当滑动到顶部和底部时,实现Item的分离效果的ListView
gundumw100
android
拉动ListView,Item之间的间距会变大,释放后恢复原样;
package cn.tangdada.tangbang.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import andr
程序员用HTML5制作的爱心树表白动画
ini
JavaScript jquery Web html5 css
体验效果:http://keleyi.com/keleyi/phtml/html5/31.htmHTML代码如下:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta charset="UTF-8" >
<ti
预装windows 8 系统GPT模式的ThinkPad T440改装64位 windows 7旗舰版
kakajw
ThinkPad 预装 改装 windows 7 windows 8
该教程具有普遍参考性,特别适用于联想的机器,其他品牌机器的处理过程也大同小异。
该教程是个人多次尝试和总结的结果,实用性强,推荐给需要的人!
缘由
小弟最近入手笔记本ThinkPad T440,但是特别不能习惯笔记本出厂预装的Windows 8系统,而且厂商自作聪明地预装了一堆没用的应用软件,消耗不少的系统资源(本本的内存为4G,系统启动完成时,物理内存占用比
Nginx学习笔记
mcj8089
nginx
一、安装nginx 1、在nginx官方网站下载一个包,下载地址是:
http://nginx.org/download/nginx-1.4.2.tar.gz
2、WinSCP(ftp上传工
mongodb 聚合查询每天论坛链接点击次数
qiaolevip
每天进步一点点 学习永无止境 mongodb 纵观千象
/* 18 */
{
"_id" : ObjectId("5596414cbe4d73a327e50274"),
"msgType" : "text",
"sendTime" : ISODate("2015-07-03T08:01:16.000Z"
java术语(PO/POJO/VO/BO/DAO/DTO)
Luob.
DAO POJO DTO po VO BO
PO(persistant object) 持久对象
在o/r 映射的时候出现的概念,如果没有o/r映射,就没有这个概念存在了.通常对应数据模型(数据库),本身还有部分业务逻辑的处理.可以看成是与数据库中的表相映射的java对象.最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合.PO中应该不包含任何对数据库的操作.
VO(value object) 值对象
通
算法复杂度
Wuaner
Algorithm
Time Complexity & Big-O:
http://stackoverflow.com/questions/487258/plain-english-explanation-of-big-o
http://bigocheatsheet.com/
http://www.sitepoint.com/time-complexity-algorithms/