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

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的端口不通...

    Python自动化办公源码-34 Python批量新建文件夹并保存日志信息

    Python自动化办公源码-34 Python批量新建文件夹并保存日志信息

    粒子滤波算法在目标跟踪中的实践与源码解析集合:多套系统源码包括基于meanshift的应用、MATLAB实现及与卡尔曼滤波比较,粒子滤波(器)滤波(器)及应用源码集合目标跟踪提取图像特征 以下多套系统

    粒子滤波算法在目标跟踪中的实践与源码解析集合:多套系统源码包括基于meanshift的应用、MATLAB实现及与卡尔曼滤波比较,粒子滤波(器)滤波(器)及应用源码集合目标跟踪提取图像特征 以下多套系统源码 1、基于meanshift和粒子滤波的目标跟踪代码 2、MATLAB编写的粒子滤波器的源代码 3、粒子滤波算法实例 4、粒子滤波mcmc介绍 5、粒子滤波算法 matlab(粒子滤波用于剩余寿命预测的实例代码 MATLAB语言编写 附有详细代码说明) 6、粒子滤波matlab代码(能够运行。 Pf粒子滤波实现的目标跟踪程序,可实现针对非高斯噪声情况下的跟踪) 7、粒子滤波代码(三个粒子滤波的演示程序,一个滤波,一个目标跟踪,一个机器人定位) 8、发个Matlab实现粒子滤波算法的程序 9、粒子滤波的简介和5中matlab程序仿真 10、粒子滤波代码与卡尔曼做比较(这是用于目标跟踪的粒子滤波代码, 用matlab编写的,很有借鉴性,一维情况下, 非高斯非线性,其中将扩展卡尔曼滤波与粒子滤波进行比较,更好的说明了粒子滤波的优越性) 11、EKF,UKF和PF粒子滤波的性能(对比分析EKF

    基于java+ssm+mysql的数学竞赛网站 源码+数据库+论文(高分毕设项目).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql8.0 部署环境:Tomcat(建议用 7.x 或者 8.x 版本),maven 数据库工具:navicat

    西门子PLC与三菱变频器通讯程序:触摸屏控制变频器实现精准频率调节与实时监控,西门子1200 PLC与3台三菱E700变频器通讯程序 器件:西门子1200 PLC,3台三菱E700变频

    西门子PLC与三菱变频器通讯程序:触摸屏控制变频器实现精准频率调节与实时监控,西门子1200 PLC与3台三菱E700变频器通讯程序 器件:西门子1200 PLC,3台三菱E700变频器,西门子KTP700 Basic Pn触摸屏,昆仑通态触摸屏(带以太网),中途可以加路由器 控制方式:触摸屏与plc以太网通讯,PLC与变频器通讯485口相连 功能:触摸屏控制变频器设定频率,启停,读取输出频率,电压 说明:是程序,非硬件。 程序有注释,西门子触摸屏程序,昆仑通态程序,变频器设置,接线都有,一应俱全 ,核心关键词:西门子1200 PLC; 三菱E700变频器; 通讯程序; 触摸屏控制; 设定频率; 启停; 读取输出频率; 电压; PLC与变频器485口相连; 程序注释; 西门子触摸屏程序; 昆仑通态程序; 变频器设置; 接线。,基于西门子PLC与三菱变频器通讯的控制系统程序

    Python自动化办公源码-35Python从Excel表中批量复制粘贴数据到新表

    Python自动化办公源码-35Python从Excel表中批量复制粘贴数据到新表

    基于Spring Boot + Vue框架的出租车管理系统设计源码

    本项目为基于Spring Boot和Vue框架构建的出租车管理系统源码,总计包含118个文件,涵盖99个Java源文件、15个XML配置文件、1个Git忽略文件、1个JAR包文件以及1个Markdown文件。该系统采用Java语言开发,适用于出租车行业的智能化管理需求。

    基于滑膜与PID控制的分布式电动汽车动态载荷分配与操稳控制优化策略,滑膜+pid+上层设计下层平均分配 优化分配 动态载荷分配,分布式电动汽车操稳控制 本研究在matlab simulink建立七自由

    基于滑膜与PID控制的分布式电动汽车动态载荷分配与操稳控制优化策略,滑膜+pid+上层设计下层平均分配 优化分配 动态载荷分配,分布式电动汽车操稳控制 本研究在matlab simulink建立七自由度轮毂电机驱动汽车模型,使用滑膜联合控制以及pid控制实现转向过程中质心侧偏角,橫摆角速度,滑转率稳定性控制, ,关键词:滑膜控制; PID控制; 七自由度轮毂电机驱动汽车模型; 联合控制; 稳定性控制; 动态载荷分配; 操稳控制; 分布式电动汽车。,"基于滑膜与PID控制的分布式电动汽车操稳控制与动态载荷分配研究"

    单相PWM整流器两种控制策略仿真研究:PR控制器与PI控制器的应用及性能分析,单相PWM整流器两种控制策略实现(交流220V-直流350V整流)仿真,分别采用直接电流控制(PR控制器)与dq控制(PI

    单相PWM整流器两种控制策略仿真研究:PR控制器与PI控制器的应用及性能分析,单相PWM整流器两种控制策略实现(交流220V-直流350V整流)仿真,分别采用直接电流控制(PR控制器)与dq控制(PI控制器)实现,两个仿真动稳态性能良好,附带仿真介绍文档,详细讲述仿真搭建过程,并附带参考文献与原理出处,内容详实,适合电力电子入门仿真参考。 ,核心关键词:单相PWM整流器;控制策略;直接电流控制(PR控制器);虚拟dq控制(PI控制器);仿真动稳态性能;仿真介绍文档;仿真搭建过程;参考文献;原理出处;电力电子入门仿真参考。,《单相PWM整流器仿真:直接电流与虚拟dq控制策略对比解析》

    chromedriver-win64-125版本所有资源打包:125.0.6368.0 -125.0.6422.78之间的所有版本打包下载

    chromedriver-win64-125版本所有资源打包:125.0.6368.0 -125.0.6422.78之间的所有版本打包下载

    基于java+ssm+mysql的二手车交易平台 源码+数据库+论文(高分毕设项目).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql8.0 部署环境:Tomcat(建议用 7.x 或者 8.x 版本),maven 数据库工具:navicat

    IEEE69节点系统Simulink仿真模型:潮流计算与故障分析及分布式电源影响观察,IEEE69节点系统Simulink仿真 1.基础功能:基于Matlab simulink平台搭建IEEE33节点

    IEEE69节点系统Simulink仿真模型:潮流计算与故障分析及分布式电源影响观察,IEEE69节点系统Simulink仿真 1.基础功能:基于Matlab simulink平台搭建IEEE33节点仿真模型,对电力系统进行潮流计算 2.拓展功能: 可在该IEEE69节系统仿真模型上进行故障分析(短路,断线等),也可以在该模型上接入分布式电源,观察分布式电源接入对系统的影响。 ,IEEE69节点系统Simulink仿真; Matlab Simulink平台; 潮流计算; 故障分析; 分布式电源接入影响; 短路分析; 断线分析。,Matlab Simulink中IEEE 69节点系统仿真:功能扩展与分布式电源接入影响分析

    2025最新人工智能与健康题库及答案.docx

    2025最新人工智能与健康题库及答案.docx

    蛇优化算法SO优化LSSVM的c和g参数在多输入单输出下的拟合预测建模-Matlab程序实现与结果展示,蛇优化算法SO优化支持向量机LSSVM的c和g参数做多输入单输出的拟合预测建模 程序内注释详

    蛇优化算法SO优化LSSVM的c和g参数在多输入单输出下的拟合预测建模——Matlab程序实现与结果展示,蛇优化算法SO优化支持向量机LSSVM的c和g参数做多输入单输出的拟合预测建模。 程序内注释详细直接替数据就可以使用。 程序语言为matlab。 程序直接运行可以出拟合预测图,迭代优化图,线性拟合预测图,多个预测评价指标。 PS:以下效果图为测试数据的效果图,主要目的是为了显示程序运行可以出的结果图,具体预测效果以个人的具体数据为准。 2.由于每个人的数据都是独一无二的,因此无法做到可以任何人的数据直接替就可以得到自己满意的效果。 ,蛇优化算法; SO优化; 支持向量机; LSSVM; c和g参数; 多输入单输出; 拟合预测建模; MATLAB程序; 拟合预测图; 迭代优化图; 线性拟合预测图; 预测评价指标。,基于蛇优化算法与SO优化的LSSVM多输入单输出预测建模及可视化工具

    基于Django+Yolov8+Tensorflow的智能鸟类识别平台

    系统旨在为了帮助鸟类爱好者、学者、动物保护协会等群体更好的了解和保护鸟类动物。用户群体可以通过平台采集野外鸟类的保护动物照片和视频,甄别分类、实况分析鸟类保护动物,与全世界各地的用户,沟通交流。使用方法请参考文章:https://blog.csdn.net/m0_64027967/article/details/145266648

    http和https的区别?(保姆级教程)

    http和https的区别?【http和https有什么区别?实现网站https化-让网站更安全!简介】 内容概要: 本文为初学者提供了一个全面的学习指南,通过通俗易懂的语言,介绍了http和https的区别,以及如何实现网站https化。本文旨在帮助读者理解网站安全性的重要性,并掌握网站https化的实现方法。 适用人群: 本篇文章主要面向初学者,特别是那些对网络安全感兴趣的读者。它为读者提供了一个实用的指南,帮助他们了解网站安全的基本概念和方法。 使用场景及目标: 本篇文章适用于那些想要提高自己网站安全性的读者。它的目标是帮助读者理解https的作用,掌握实现网站https化的方法和步骤,从而保护自己的网站和用户的安全。 其他说明: 文章采用通俗易懂、口语化的语言风格,旨在让读者轻松理解并吸收内容。同时,文章还强调了实际操作的重要性,鼓励读者亲自动手实践,提高自己的网络安全技能。最后,文章鼓励读者保持积极的心态,将网络安全作为一项重要的技能进行学习和实践。

    基于FATFS系统的STM32F407 SD卡升级Bootloader程序:自动检测与升级流程,stm32f407 SD卡升级 bootloader程序 基于sdio fatfs系统的stm32 b

    基于FATFS系统的STM32F407 SD卡升级Bootloader程序:自动检测与升级流程,stm32f407 SD卡升级 bootloader程序 基于sdio fatfs系统的stm32 bootloader程序 功能简介: 本程序使用fatfs系统读取bin文件。 开机后会自动检测sd卡,检测到sd卡后,再读取固定名称的bin文件,之后会对bin文件进行首包校验,判断该升级包的起始地址是否正确,正确的话,就循环读取bin文件并写入到flash中。 完成升级。 详细流程请看流程图 ,stm32f407; SD卡升级; bootloader程序; fatfs系统读取bin文件; 检测SD卡; 首包校验; 循环写入flash。,STM32F407 SD卡升级Bootloader程序:基于SDIO FATFS系统实现自动升级功能

    Python自动化办公源码-16从Excel总表中分割10个部门的年假数据

    Python自动化办公源码-16从Excel总表中分割10个部门的年假数据

    大规模MIMO-D2D蜂窝网络中的下行传输速率研究.pdf

    大规模MIMO-D2D蜂窝网络中的下行传输速率研究.pdf

Global site tag (gtag.js) - Google Analytics