`
Donald_Draper
  • 浏览: 984513 次
社区版块
存档分类
最新评论

SocketChannelImpl 解析三(接收数据)

    博客分类:
  • NIO
nio 
阅读更多
SocketChannelImpl 解析一(通道连接,发送数据):http://donald-draper.iteye.com/blog/2372364
SocketChannelImpl 解析二(发送数据后续):http://donald-draper.iteye.com/blog/2372548
引言:
前一篇文章我们看了一下SocketChannelImpl发送多个字节序列的过程,先来回顾一下:
SocketChannelImpl写ByteBuffer数组方法,首先同步写锁,确保通道,输出流打开,连接建立委托给IOUtil,将ByteBuffer数组写到输出流中,这一过程为获取存放i个字节缓冲区的IOVecWrapper,遍历ByteBuffer数组m,将字节缓冲区添加到iovecwrapper的字节缓冲区数组中,如果ByteBuffer非Direct类型,委托Util从当前线程的缓冲区获取容量为j2临时DirectByteBuffer,并将ByteBuffer写到DirectByteBuffer,并将DirectByteBuffer添加到iovecwrapper的字节缓冲区(Shadow-Direct)数组中,将字节缓冲区的起始地址写到iovecwrapper,字节缓冲区的实际容量写到iovecwrapper;遍历iovecwrapper的字节缓冲区(Shadow-Direct)数组,将Shadow数组中的DirectByteBuffer通过Util添加到本地线程的缓存区中,并清除DirectByteBuffer在iovecwrapper的相应数组中的信息;最后通过
SocketDispatcher,将iovecwrapper的缓冲区数据,写到filedescriptor对应的输出流中。
今天我们来看一下接受数据
再来看SocketChannelImpl的读操作
 public int read(ByteBuffer bytebuffer)
        throws IOException
    {
        if(bytebuffer == null)
            throw new NullPointerException();
        Object obj = readLock;//获取读锁
        JVM INSTR monitorenter ;//进入同步,try
        if(!ensureReadOpen())//确保通道,输入流打开,通道连接建立
            return -1;
        int i = 0;
        begin();
        int k;
        Object obj3;
        Exception exception;
        synchronized(stateLock)
        {
            if(isOpen())
                break MISSING_BLOCK_LABEL_146;
            k = 0;
        }
	//清除读线程
        readerCleanup();
        end(i > 0 || i == -2);
        obj3 = stateLock;
        JVM INSTR monitorenter ;
        if(i > 0 || isInputOpen) goto _L2; else goto _L1
_L1:
        -1;
        obj;
        JVM INSTR monitorexit ;
        return;
_L2:
        obj3;
        JVM INSTR monitorexit ;
          goto _L3
        exception;
        obj3;
        JVM INSTR monitorexit ;
        throw exception;
_L3:
        if(!$assertionsDisabled && !IOStatus.check(i))
            throw new AssertionError();
        obj;
        JVM INSTR monitorexit ;
        return k;
	//初始化本地读线程
        readerThread = NativeThread.current();
        obj1;
        JVM INSTR monitorexit ;
        int j;
        do
	   //委托IOUtil从输入流读取字节序列,写到bytebuffer
            i = IOUtil.read(fd, bytebuffer, -1L, nd, readLock);
        while(i == -3 && isOpen());
        ...
 }

从输入流读取字节序列,写到buffer,有几点要关注
1.
 if(!ensureReadOpen())//确保通道,输入流打开,通道连接建立
            return -1;

2.
//清除读线程
readerCleanup();

3.
do
   //委托IOUtil从输入流读取字节序列,写到bytebuffer
    i = IOUtil.read(fd, bytebuffer, -1L, nd, readLock);
while(i == -3 && isOpen());

下面我们分别来看这几点:
1.
 if(!ensureReadOpen())//确保通道,输入流打开,通道连接建立
            return -1;

//确保通道,输入流打开,通道连接建立
 private boolean ensureReadOpen()
        throws ClosedChannelException
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())//通道打开
            throw new ClosedChannelException();
        if(!isConnected())//连接建立
            throw new NotYetConnectedException();
        if(!isInputOpen)//输入流打开
            return false;
        true;
        obj;
        JVM INSTR monitorexit ;
        return;
        Exception exception;
        exception;
        throw exception;
    }

2.
//清除读线程
readerCleanup();

private void readerCleanup()
      throws IOException
  {
      //同步通道状态锁,清除读线程,如果通道关闭则,执行清除工作
      synchronized(stateLock)
      {
          readerThread = 0L;
          if(state == 3)
              kill();//这个后面再讲
      }
  }

3.
do
   //委托IOUtil从输入流读取字节序列,写到bytebuffer
    i = IOUtil.read(fd, bytebuffer, -1L, nd, readLock);
while(i == -3 && isOpen());

这里循环的原因,线程读输入流,有可能因为某种原因被中断,中断位消除,继续读取输入流,写到buffer
//IOUtil
static int read(FileDescriptor filedescriptor, ByteBuffer bytebuffer, long l, NativeDispatcher nativedispatcher, Object obj)
       throws IOException
   {
       ByteBuffer bytebuffer1;
       //如果buffer为只读,则抛出IllegalArgumentException
       if(bytebuffer.isReadOnly())
           throw new IllegalArgumentException("Read-only buffer");
       //如果buffer为DirectBuffer,则委托给readIntoNativeBuffer
       if(bytebuffer instanceof DirectBuffer)
           return readIntoNativeBuffer(filedescriptor, bytebuffer, l, nativedispatcher, obj);
       //从当前线程缓存区获取临时的DirectByteBuffer
       bytebuffer1 = Util.getTemporaryDirectBuffer(bytebuffer.remaining());
       int j;
       //委托readIntoNativeBuffer方法,读取输入流数据,到临时DirectByteBuffer
       int i = readIntoNativeBuffer(filedescriptor, bytebuffer1, l, nativedispatcher, obj);
       //读写模式切换
       bytebuffer1.flip();
       if(i > 0)
           //如果有数据被读取,则放到byteBuffer中
           bytebuffer.put(bytebuffer1);
       j = i;//记录读取的字节数
       //添加临时DirectByteBuffer到当前线程的缓冲区,以便重用,
       //因为重新DirectByteBuffer为直接操作物理内存,频繁分配物理内存,将耗费过多的资源。
       Util.offerFirstTemporaryDirectBuffer(bytebuffer1);
       return j;
       Exception exception;
       exception;
       Util.offerFirstTemporaryDirectBuffer(bytebuffer1);
       throw exception;
   }

来看readIntoNativeBuffer方法
private static int readIntoNativeBuffer(FileDescriptor filedescriptor, ByteBuffer bytebuffer, long l, NativeDispatcher nativedispatcher, Object obj)
        throws IOException
    {
        int i = bytebuffer.position();
        int j = bytebuffer.limit();
	//如果断言开启,buffer的position大于limit,则抛出断言错误
        if(!$assertionsDisabled && i > j)
            throw new AssertionError();
	//获取需要读的字节数
        int k = i > j ? 0 : j - i;
        if(k == 0)
            return 0;
        int i1 = 0;
	//从输入流读取k个字节到buffer
        if(l != -1L)
            i1 = nativedispatcher.pread(filedescriptor, ((DirectBuffer)bytebuffer).address() + (long)i, k, l, obj);
        else
            i1 = nativedispatcher.read(filedescriptor, ((DirectBuffer)bytebuffer).address() + (long)i, k);
	//重新定位buffer的position
        if(i1 > 0)
            bytebuffer.position(i + i1);
        return i1;
    }

readIntoNativeBuffer方法中一点我们需要关注:
//从输入流读取k个字节到buffer
if(l != -1L)
    i1 = nativedispatcher.pread(filedescriptor, ((DirectBuffer)bytebuffer).address() + (long)i, k, l, obj);
else
    i1 = nativedispatcher.read(filedescriptor, ((DirectBuffer)bytebuffer).address() + (long)i, k);

//NativeDispatcher
 int pread(FileDescriptor filedescriptor, long l, int i, long l1, Object obj)
        throws IOException
    {
        throw new IOException("Operation Unsupported");
    }

从NativeDispatcher的pread方法可以看出,当前JDK版本,还不支持pread操作,我的JDK版本为1.7.0.17。
//SocketDispatcher
 int read(FileDescriptor filedescriptor, long l, int i)
        throws IOException
    {
        return read0(filedescriptor, l, i);
    }
 static native int read0(FileDescriptor filedescriptor, long l, int i)
        throws IOException;

至此读输入流到buffer,已经看完,首先同步读写,确保通道,输入流打开,通道连接建立,
清除原始读线程,获取新的本地读线程,委托IOUtil读输入流到buffer;IOUtil读输入流到buffer,首先确保buffer是可写的,否则抛出IllegalArgumentException,然后判断buffer是否为Direct类型,是则委托给readIntoNativeBuffer,否则通过Util从当前线程缓冲区获取一个临时的DirectByteBuffer,然后通过readIntoNativeBuffer读输入流数据到临时的DirectByteBuffer,这一个过程是通过SocketDispatcher的read方法实现,读写数据到DirectByteBuffer中后,将DirectByteBuffer中数据,写到原始buffer中,并将
DirectByteBuffer添加到添加临时DirectByteBuffer到当前线程的缓冲区,以便重用,
因为重新DirectByteBuffer为直接操作物理内存,频繁分配物理内存,将耗费过多的资源。
在来看从输入流读取数据,写到多个buffer:
public long read(ByteBuffer abytebuffer[], int i, int j)
        throws IOException
    {
        //校验参数
        if(i < 0 || j < 0 || i > abytebuffer.length - j)
            throw new IndexOutOfBoundsException();
        Object obj = readLock;//获取读锁
        JVM INSTR monitorenter ;//进入同步,try
        if(!ensureReadOpen())//确保通道打开,连接建立,输入流打开
            return -1L;
        long l = 0L;
        begin();//与end协同,记录中断器,处理读操作过程中的中断问题
        long l2;
        Object obj3;
        Exception exception;
        synchronized(stateLock)
        {
            if(isOpen())
                break MISSING_BLOCK_LABEL_177;
            l2 = 0L;
        }
	//清除原始读线程
        readerCleanup();
        end(l > 0L || l == -2L);
        obj3 = stateLock;
        JVM INSTR monitorenter ;
        if(l > 0L || isInputOpen) goto _L2; else goto _L1
_L1:
        -1L;
        obj;
        JVM INSTR monitorexit ;
        return;
_L2:
        obj3;
        JVM INSTR monitorexit ;
          goto _L3
        exception;
        obj3;
        JVM INSTR monitorexit ;
        throw exception;
_L3:
        if(!$assertionsDisabled && !IOStatus.check(l))
            throw new AssertionError();
        obj;
        JVM INSTR monitorexit ;
        return l2;
	//获取本地读线程
        readerThread = NativeThread.current();
        obj1;
        JVM INSTR monitorexit ;
        long l1;
        do
	    //委托给IOUtil,从输入流读取数据,写到多个buffer
            l = IOUtil.read(fd, abytebuffer, i, j, nd);
        while(l == -3L && isOpen());
        l1 = IOStatus.normalize(l);
}

从输入流读取数据,写到多个buffer,我们只需要关注下面这点
 do
    //委托给IOUtil,从输入流读取数据,写到多个buffer
    l = IOUtil.read(fd, abytebuffer, i, j, nd);
while(l == -3L && isOpen());

这里循环的原因,线程读输入流,有可能因为某种原因被中断,中断位消除,继续读取输入流,写到buffer;
//IOUtil
static long read(FileDescriptor filedescriptor, ByteBuffer abytebuffer[], int i, int j, NativeDispatcher nativedispatcher)
        throws IOException
    {
        IOVecWrapper iovecwrapper;
        boolean flag;
        int k;
	//获取存放i个byteBuffer的IOVecWrapper
        iovecwrapper = IOVecWrapper.get(j);
        flag = false;
        k = 0;
        long l1;
        int l = i + j;
        for(int i1 = i; i1 < l && k < IOV_MAX; i1++)
        {
            ByteBuffer bytebuffer = abytebuffer[i1];
            if(bytebuffer.isReadOnly())
                throw new IllegalArgumentException("Read-only buffer");
            int j1 = bytebuffer.position();
            int k1 = bytebuffer.limit();
            if(!$assertionsDisabled && j1 > k1)
                throw new AssertionError();
            int j2 = j1 > k1 ? 0 : k1 - j1;
            if(j2 <= 0)
                continue;
	   //将buffer添加到iovecwrapper的字节缓冲区数组中
            iovecwrapper.setBuffer(k, bytebuffer, j1, j2);
            if(!(bytebuffer instanceof DirectBuffer))
            {
	       //获取容量为j2临时DirectByteBuffer
                ByteBuffer bytebuffer2 = Util.getTemporaryDirectBuffer(j2);
		//添加DirectByteBuffer到iovecwrapper的shadow buffer数组
                iovecwrapper.setShadow(k, bytebuffer2);
                bytebuffer = bytebuffer2;
                j1 = bytebuffer2.position();
            }
	    //将字节缓冲区的起始地址写到iovecwrapper
            iovecwrapper.putBase(k, ((DirectBuffer)bytebuffer).address() + (long)j1);
	     //将字节缓冲区的实际容量写到iovecwrapper
            iovecwrapper.putLen(k, j2);
            k++;
        }

        if(k != 0)
            break MISSING_BLOCK_LABEL_263;
        l1 = 0L;
        if(!flag)
        {
            for(int i2 = 0; i2 < k; i2++)
            {   
	        //获取iovecwrapper索引i2对应的字节序列副本
                ByteBuffer bytebuffer1 = iovecwrapper.getShadow(i2);
                if(bytebuffer1 != null)
		//如果字节序列不为空,则添加到当前线程的缓存区中
                    Util.offerLastTemporaryDirectBuffer(bytebuffer1);
		 //清除索引i2对应的字节序列在iovecwrapper中的字节序列数组,及相应副本数组的信息
                iovecwrapper.clearRefs(i2);
            }

        }
        return l1;
        long l4;
	//委托给nativedispatcher,从filedescriptor对应的输入流读取数据,写到iovecwrapper的缓冲区中。
        long l2 = nativedispatcher.readv(filedescriptor, iovecwrapper.address, k);
}

再来看IOUtil写buffer数组的关键点
long l2 = nativedispatcher.readv(filedescriptor, iovecwrapper.address, k);

//SocketDispatcher
 long readv(FileDescriptor filedescriptor, long l, int i)
        throws IOException
    {
        return readv0(filedescriptor, l, i);
    }
     static native long readv0(FileDescriptor filedescriptor, long l, int i)
        throws IOException;

至此我们把SocketChannelImpl从输入流读取数据,写到ByteBuffer数组的read方法看完,首先同步写锁,确保通道,连接建立,输入流打开,委托给IOUtil,从输入流读取数据写到ByteBuffer数组中;IOUtil首先获取存放i个字节缓冲区的IOVecWrapper,遍历ByteBuffer数组m,将buffer添加到iovecwrapper的字节缓冲区数组中,如果ByteBuffer非Direct类型,委托Util从当前线程的缓冲区获取容量为j2临时DirectByteBuffer,并将ByteBuffer写到DirectByteBuffer,并将DirectByteBuffer添加到iovecwrapper的字节缓冲区(Shadow-Direct)数组中,将字节缓冲区的起始地址写到iovecwrapper,字节缓冲区的实际容量写到iovecwrapper;遍历iovecwrapper的字节缓冲区(Shadow-Direct)数组,将Shadow数组中的DirectByteBuffer通过Util添加到本地线程的缓存区中,并清除DirectByteBuffer在iovecwrapper的相应数组中的信息;最后通过
SocketDispatcher,从filedescriptor对应的输入流读取数据,写到iovecwrapper的缓冲区中。
总结:
    读输入流到buffer,首先同步读写,确保通道,输入流打开,通道连接建立,
清除原始读线程,获取新的本地读线程,委托IOUtil读输入流到buffer;IOUtil读输入流到buffer,首先确保buffer是可写的,否则抛出IllegalArgumentException,然后判断buffer是否为Direct类型,是则委托给readIntoNativeBuffer,否则通过Util从当前线程缓冲区获取一个临时的DirectByteBuffer,然后通过readIntoNativeBuffer读输入流数据到临时的DirectByteBuffer,这一个过程是通过SocketDispatcher的read方法实现,读写数据到DirectByteBuffer中后,将DirectByteBuffer中数据,写到原始buffer中,并将
DirectByteBuffer添加到添加临时DirectByteBuffer到当前线程的缓冲区,以便重用,因为重新DirectByteBuffer为直接操作物理内存,频繁分配物理内存,将耗费过多的资源。
    从输入流读取数据,写到ByteBuffer数组的read方法,首先同步写锁,确保通道,连接建立,输入流打开,委托给IOUtil,从输入流读取数据写到ByteBuffer数组中;IOUtil首先获取存放i个字节缓冲区的IOVecWrapper,遍历ByteBuffer数组m,将buffer添加到iovecwrapper的字节缓冲区数组中,如果ByteBuffer非Direct类型,委托Util从当前线程的缓冲区获取容量为j2临时DirectByteBuffer,并将ByteBuffer写到DirectByteBuffer,并将DirectByteBuffer添加到iovecwrapper的字节缓冲区(Shadow-Direct)数组中,将字节缓冲区的起始地址写到iovecwrapper,字节缓冲区的实际容量写到iovecwrapper;遍历iovecwrapper的字节缓冲区(Shadow-Direct)数组,将Shadow数组中的DirectByteBuffer通过Util添加到本地线程的缓存区中,并清除DirectByteBuffer在iovecwrapper的相应数组中的信息;最后通过
SocketDispatcher,从filedescriptor对应的输入流读取数据,写到iovecwrapper的缓冲区中。

SocketChannelImpl 解析四(关闭通道等):http://donald-draper.iteye.com/blog/2372717
分享到:
评论

相关推荐

    jdk 1.8源码包,包含sun源码,绝对真实,自己看大小就懂了,靠谱

    此外,Stream API也是1.8的一大亮点,它提供了处理集合的新方式,可以进行数据流的过滤、映射、归约等操作,大大简化了数据处理的代码。 在"jdk-5b86f66575b7"这个压缩包中,很可能包含了JDK 1.8的完整源码,包括...

    Ice-3.7.4.msi for windows版

    at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:574) at IceInternal.Network.doFinishConnect(Network.java:393) ... 6 more 这种报错是ICE服务端没有起来,telnet服务端ICE的端口不通...

    YOLO算法-数据集数据集-330张图像带标签-椅子-书桌.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    java毕设项目之ssm蜀都天香酒楼的网站设计与实现+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    weixin138社区互助养老+ssm(论文+源码)-kaic.zip

    weixin138社区互助养老+ssm(论文+源码)_kaic.zip

    光纤到户及通信基础设施报装申请表.docx

    光纤到户及通信基础设施报装申请表.docx

    java毕设项目之ssm基于jsp的精品酒销售管理系统+jsp(完整前后端+说明文档+mysql+lw).zip

    项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 服务器:tomcat7

    功能完善的电商数据智能爬虫采集系统项目全套技术资料.zip

    功能完善的电商数据智能爬虫采集系统项目全套技术资料.zip

    YOLO算法-刀数据集-198张图像带标签-刀-枪.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    Android程序开发初级教程WORD文档doc格式最新版本

    ### Android程序开发初级教程(一):初识Android **平台概述** Google推出的Android操作系统平台已经正式亮相,这是一个基于Linux内核的开源操作系统。对于开发者而言,了解其架构和支持的开发语言至关重要。以下是Android平台的架构概览: **平台架构及功能** 1. **应用框架(Application Framework)**:包含可重用和可替换的组件,确保所有软件在该层面上的平等性。 2. **Dalvik虚拟机(Dalvik Virtual Machine)**:一个基于Linux的虚拟机,为Android应用提供运行环境。 3. **集成浏览器(Integrated Browser)**:基于开源WebKit引擎的浏览器,位于应用层。 4. **优化图形(Optimized Graphics)**:包括自定义的2D图形库和遵循OpenGL ES 1.0标准的3D实现。 5. **SQLite数据库**:用于数据存储。 6. **多媒体支持(Media Support)**:支持通用音频、视频以及多种图片格式(如MPEG4, H.264

    【组合数学答案】组合数学-苏大李凡长版-课后习题答案

    内容概要:本文档是《组合数学答案-网络流传版.pdf》的内容,主要包含了排列组合的基础知识以及一些经典的组合数学题目。这些题目涵盖了从排列数计算、二项式定理的应用到容斥原理的实际应用等方面。通过对这些题目的解析,帮助读者加深对组合数学概念和技巧的理解。 适用人群:适合初学者和有一定基础的学习者。 使用场景及目标:可以在学习组合数学课程时作为练习题参考,也可以在复习考试或准备竞赛时使用,目的是提高解决组合数学问题的能力。 其他说明:文档中的题目覆盖了组合数学的基本知识点,适合逐步深入学习。每个题目都有详细的解答步骤,有助于读者掌握解题思路和方法。

    .net core mvc在线考试系统asp.net考试系统源码考试管理系统 主要技术: 基于.net core mvc架构和sql server数据库,数据库访问采用EF core code fir

    .net core mvc在线考试系统asp.net考试系统源码考试管理系统 主要技术: 基于.net core mvc架构和sql server数据库,数据库访问采用EF core code first,前端采用vue.js和bootstrap。 功能模块: 系统包括前台和后台两个部分,分三种角色登录。 管理员登录后台,拥有科目管理,题库管理,考试管理,成绩管理,用户管理等功能。 教师登录后台,可进行题库管理,考试管理和成绩管理。 用户登录前台,可查看考试列表,参加考试,查看已考试的结果,修改密码等。 系统实现了国际化,支持中英两种语言。 源码打包: 包含全套源码,数据库文件,需求分析和代码说明文档。 运行环境: 运行需vs2019或者以上版本,sql server2012或者以上版本。

    YOLO算法-易拉罐识别数据集-512张图像带标签-可口可乐.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    (175415460)基于SpringBoot的通用管理系统源码+数据库+项目文档,前后端分离的通用管理系统模版,可用于开发毕业设计

    包含了登陆注册、用户管理、部门管理、文件管理、权限管理、日志管理、个人中心、数据字典和代码生成这九个功能模块 系统采用了基于角色的访问控制,角色和菜单关联,一个角色可以配置多个菜单权限;然后再将用户和角色关联,一位用户可以赋予多个角色。这样用户就可以根据角色拿到该有的菜单权限,更方便管理者进行权限管控。 本系统还封装了文件管理功能,在其他模块如若要实现图片/文件上传预览时,前端只需导入现成的 Vue 组件即可实现(使用 viewerjs 依赖实现),后端只需定义 String 类型的实体类变量即可,无需再去研究文件上传预览的相关功能,简化了开发者的工作量。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    三相10Kw光伏并网逆变器 包含全套理图 PCB 源代码

    三相10Kw光伏并网逆变器。包含全套理图 PCB 源代码

    GJB 5236-2004 军用软件质量度量

    GJB 5236-2004 军用软件质量度量文档,本称准规定了车用软件产品的质重模型和基本的度量。本标准为确定车用软件质量需求和衡量军用 软件产品的能力提供了一个框架。

    (179941432)基于MATLAB车牌识别系统【GUI含界面】.zip

    基于MATLAB车牌识别系统【GUI含界面】.zip。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    (9546452)宿舍管理系统

    【宿舍管理系统】是一种专为高校或住宿机构设计的信息化解决方案,旨在提高宿舍管理的效率和准确性。该系统包含了多项核心功能,如宿舍管理员管理、宿舍信息维护、查询、卫生检查以及电费缴纳等,旨在实现全面的宿舍运营自动化。 **宿舍管理员管理**功能允许指定的管理员进行用户权限分配和角色设定。这包括对管理员账户的创建、修改和删除,以及设置不同的操作权限,例如只读、编辑或管理员权限。通过这样的权限控制,可以确保数据的安全性和管理的规范性。 **宿舍添加与管理**是系统的基础模块。管理员可以录入宿舍的基本信息,如宿舍号、楼栋、楼层、房间类型(单人间、双人间等)、容纳人数、设施配置等。此外,系统还支持批量导入或导出宿舍信息,方便数据的备份和迁移。 **查询功能**是系统的重要组成部分,它允许管理员和学生根据不同的条件(如宿舍号、楼栋、学生姓名等)快速查找宿舍信息。此外,系统还可以生成各种统计报告,如宿舍占用率、空闲宿舍数量等,以便于决策者进行资源优化。 **卫生检查**功能则是对宿舍卫生状况进行定期评估。管理员可设定检查计划,包括检查周期、评分标准等,并记录每次检查的结果。系统能自动生成卫生报表,用于

    YOLO算法-包装好的服装数据集-654张图像带标签-.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    九缸星形发动机点火器3D

    九缸星形发动机点火器3D

Global site tag (gtag.js) - Google Analytics