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

WindowsSelectorImpl解析一(FdMap,PollArrayWrapper)

    博客分类:
  • NIO
阅读更多
Channel接口定义:http://donald-draper.iteye.com/blog/2369111
AbstractInterruptibleChannel接口定义:http://donald-draper.iteye.com/blog/2369238
SelectableChannel接口定义:http://donald-draper.iteye.com/blog/2369317
SelectionKey定义:http://donald-draper.iteye.com/blog/2369499
SelectorProvider定义:http://donald-draper.iteye.com/blog/2369615
AbstractSelectableChannel定义:http://donald-draper.iteye.com/blog/2369742
NetworkChannel接口定义:http://donald-draper.iteye.com/blog/2369773
ServerSocketChannel定义:http://donald-draper.iteye.com/blog/2369836
Selector定义:http://donald-draper.iteye.com/blog/2370015
AbstractSelector定义:http://donald-draper.iteye.com/blog/2370138
SelectorImpl分析 :http://donald-draper.iteye.com/blog/2370519
引言:
在上一篇文章中,我们看了SelectorImpl的相关key集合和方法,先来回顾一下:
     SelectorImpl有4个集合分别为就绪key集合,key集合,key集合的代理publicKeys及就绪key集合的代理publicSelectedKeys;实际是两个集合就绪key集合和key集合,publicSelectedKeys和publicKeys是其他线程访问上述两个集合的代理。
     SelectorImpl构造的时候,初始化选择器提供者SelectorProvider,创建就绪key集合和key集合,然后初始化就绪key和key集合的代理,初始化过程为,如果nio包的JDK版本存在bug问题,则就绪key和key集合的代理集合直接引用就绪key和key集合。否则将当前key集合包装成不可修改的代理集合publicKes,将就绪key集合包装成容量固定的集合publicSelectedKeys。
其他线程获取选择器的就绪key和key集合,实际上返回的是key集合的代理publicKeys和就绪key集合的代理publicSelectedKeys。
     select方法的3中操作形式,实际上委托给为lockAndDoSelect方法,方法实际上是同步的,可安全访问,获取key集合代理publicKeys和就绪key代理集合publicSelectedKeys,然后交给doSelect(long l)方法,这个方法为抽象方法,待子类扩展。实际的关闭选择器操作implCloseSelector方法,首先唤醒等待选择操作的线程,唤醒方法wakeup待实现,同步选择器,就绪key和key集合的代理publicKeys,publicSelectedKeys,调用implClose完成实际的关闭通道工作,待子类实现。
     可选通道注册方法,首先注册的通道必须是AbstractSelectableChannel类型,并且是SelChImpl实例。更具可选择通道和选择器构造选择key,设置选择key的附加物,同步key集合代理,调用implRegister方法完成实际的注册工作,implRegister方法待子类实现。
     processDeregisterQueue方法,主要是遍历取消key集合,反注册取消key,实际的反注册工作由implDereg方法,implDereg方法待子类扩展。成功,则从集合中移除。
今天我们来看的选择器的具体实现WindowsSelectorProvider,在这篇文章中,我们要关注的是这几个方法,选择操作中的doSelect(long l),注册key操作的implRegister方法,处理取消key集合方法中implDereg方法和唤醒方法wakeup。
我们先从打开选择器开始
//Selector
  public static Selector open() throws IOException {
        return SelectorProvider.provider().openSelector();
    }

//SelectorProvider
  
 public static SelectorProvider provider() {  
            synchronized (lock) {  
                if (provider != null)  
                    return provider;  
           //在与当前线程相同访问控制权限的环境中,加载SelectorProvider实例  
                return AccessController.doPrivileged(  
                    new PrivilegedAction<SelectorProvider>() {  
                        public SelectorProvider run() {  
                                if (loadProviderFromProperty())  
                        //获取系统配置的SelectorProvider  
                                    return provider;  
                                if (loadProviderAsService())  
                         //获取类加载路径下的SelectorProvider  
                                    return provider;  
                        //加载默认的SelectorProvider  
                                provider = sun.nio.ch.DefaultSelectorProvider.create();  
                                return provider;  
                            }  
                        });  
            }  
    }  

来看默认的DefaultSelectorProvider
//DefaultSelectorProvider
 
  package sun.nio.ch;  
      
    import java.nio.channels.spi.SelectorProvider;  
      
    // Referenced classes of package sun.nio.ch:  
    //            WindowsSelectorProvider  
      
    public class DefaultSelectorProvider  
    {  
        private DefaultSelectorProvider()  
        {  
        }  
        public static SelectorProvider create()  
        {  
            //默认的WindowsSelectorProvider  
            return new WindowsSelectorProvider();  
        }  
    }  

从上面了可以看出选择器的默认实现为WindowsSelectorImpl,下面我们来具体看一下,先看一下变量的定义,具体每个变量及集合含义我们现在可能不完全解释清楚,一般从字面上可以看出它的意思,对于不能完全理解的变量,我们在后面的文章中,再纠正。
final class WindowsSelectorImpl extends SelectorImpl
{
    private final int INIT_CAP = 8;//选择key集合,key包装集合初始化容量
    private static final int MAX_SELECTABLE_FDS = 1024;//最大选择key数量
    private SelectionKeyImpl channelArray[];//选择器关联通道集合
    private PollArrayWrapper pollWrapper;//存放所有文件描述对象(选择key,唤醒管道的源与sink通道)的集合
    private int totalChannels;//注册到选择的通道数量
    private int threadsCount;//选择线程数
    private final List threads = new ArrayList();//选择操作线程集合
    private final Pipe wakeupPipe = Pipe.open();//唤醒等待选择操操的管道
    private final int wakeupSourceFd;//唤醒管道源通道文件描述
    private final int wakeupSinkFd;//唤醒管道sink通道文件描述
    private Object closeLock;//选择器关闭同步锁
    private final FdMap fdMap = new FdMap();//存放选择key文件描述与选择key映射关系的Map
    private final SubSelector subSelector = new SubSelector();//子选择器
    private long timeout;//超时时间,具体什么意思,现在还没明白,在后面在看
    private final Object interruptLock = new Object();//中断同步锁,在唤醒选择操作线程时,用于同步
    private volatile boolean interruptTriggered;//是否唤醒等待选择操的线程
    private final StartLock startLock = new StartLock();//选择操作开始锁
    private final FinishLock finishLock = new FinishLock();//选择操作结束锁
    private long updateCount;//更新数量,具体什么意思,现在还没明白,在后面在看
    static final boolean $assertionsDisabled = !sun/nio/ch/WindowsSelectorImpl.desiredAssertionStatus();
    static 
    {
        //加载nio,net资源库
        Util.load();
    } 
}

//Util
static void load()
    {
label0:
        {
            synchronized(sun/nio/ch/Util)
            {
                if(!loaded)
                    break label0;
            }
            return;
        }
        loaded = true;
	//在与当前线程相同访问控制权限的情况下,加载net和nio资源库
        AccessController.doPrivileged(new LoadLibraryAction("net"));
        AccessController.doPrivileged(new LoadLibraryAction("nio"));
        IOUtil.initIDs();
        local;
        JVM INSTR monitorexit ;
          goto _L1
        exception;
        throw exception;
_L1:
    }


//这个我们先放在这里,我们慢慢解开选择的构造
 WindowsSelectorImpl(SelectorProvider selectorprovider)
        throws IOException
    {
        super(selectorprovider);
        channelArray = new SelectionKeyImpl[8];
        totalChannels = 1;
        threadsCount = 0;
        closeLock = new Object();
        interruptTriggered = false;
        updateCount = 0L;
        pollWrapper = new PollArrayWrapper(8);
        wakeupSourceFd = ((SelChImpl)wakeupPipe.source()).getFDVal();
        SinkChannelImpl sinkchannelimpl = (SinkChannelImpl)wakeupPipe.sink();
        sinkchannelimpl.sc.socket().setTcpNoDelay(true);
        wakeupSinkFd = sinkchannelimpl.getFDVal();
        pollWrapper.addWakeupSocket(wakeupSourceFd, 0);
    }

为了更好的理解fdMap和pollWrapper作用我们来看一下这两个集合的定义:
先看FdMap
//key与key描述符映射关系Map
 private static final class FdMap extends HashMap
    {
        static final long serialVersionUID = 0L;
        private FdMap()
        {
        }
	//根据key文件描述id获取key
        private MapEntry get(int i)
        {
            return (MapEntry)get(new Integer(i));
        }
	//添加key
        private MapEntry put(SelectionKeyImpl selectionkeyimpl)
        {
            return (MapEntry)put(new Integer(selectionkeyimpl.channel.getFDVal()), new MapEntry(selectionkeyimpl));
        }
	//移除选择key
        private MapEntry remove(SelectionKeyImpl selectionkeyimpl)
        {
            Integer integer = new Integer(selectionkeyimpl.channel.getFDVal());
            MapEntry mapentry = (MapEntry)get(integer);
            if(mapentry != null && mapentry.ski.channel == selectionkeyimpl.channel)
                return (MapEntry)remove(integer);
            else
                return null;
        }
    }
    

//MapEntry
 
  private static final class MapEntry
    {
        SelectionKeyImpl ski;//选择key
	//这两个计数器,现在还不知道干什么用的,后备碰到再说
        long updateCount;//操作事件更新计数器
        long clearedCount;操作事件清除计数器
        MapEntry(SelectionKeyImpl selectionkeyimpl)
        {
            updateCount = 0L;
            clearedCount = 0L;
            ski = selectionkeyimpl;
        }
    }

从上面可以看出FdMap主要是存储选择key的,FdMap实际上是一个HashMap,key为选择key的文件描述id,value为MapEntry,MapEntry为选择key的包装Entry,里面含有更新计数器updateCount和清除计数器clearedCount。

再看PollArrayWrapper,
PollArrayWrapper,我们可以这么理解为本地内存空间管理器主要是
将文件描述(选择key,唤醒管道的source和sink通道)信息及相关的兴趣操作事件存储在本地内存空间中。PollArrayWrapper是通过AllocatedNativeObject来操作底层存储空间
//PollArrayWrapper
class PollArrayWrapper
{
    private AllocatedNativeObject pollArray;//底层内存空间
    long pollArrayAddress;//内存空间起始位置
    private static final short FD_OFFSET = 0;文件描述id开始位置
    private static final short EVENT_OFFSET = 4;//兴趣事件开始位置
    static short SIZE_POLLFD = 8;//文件描述id的长度int(4)+操作事件长度4
    //这些事件当前不能明白意思,只是简单的猜测,理解的网友给我留言,谢谢
    static final short POLLIN = 1;//添加事件
    static final short POLLOUT = 4;//拉取事件
    static final short POLLERR = 8;//操作错误
    static final short POLLHUP = 16;//操作挂起
    static final short POLLNVAL = 32;
    static final short POLLREMOVE = 2048;//移除
    static final short POLLCONN = 2;//
    private int size;
    //创建i容量的文件描述管理器
    PollArrayWrapper(int i)
    {
        int j = i * SIZE_POLLFD;
	//分配内存空间
        pollArray = new AllocatedNativeObject(j, true);
	//初始化空间起始地址
        pollArrayAddress = pollArray.address();
        size = i;//初始化容量
    }
}

//已分配的本地空间
class AllocatedNativeObject extends NativeObject
{
    AllocatedNativeObject(int i, boolean flag)
    {
        super(i, flag);
    }
    //释放本地对象空间
    synchronized void free()
    {
        //如果已分配的地址不为0,则释放空间
        if(allocationAddress != 0L)
        {
            unsafe.freeMemory(allocationAddress);
            allocationAddress = 0L;
        }
    }
}


//NativeObject,本地内存管理对象
package sun.nio.ch;
import java.nio.ByteOrder;
import sun.misc.Unsafe;

class NativeObject
{
    protected static final Unsafe unsafe = Unsafe.getUnsafe();
    protected long allocationAddress;//已分配的地址空间
    private final long address;//空间起始位置
    private static ByteOrder byteOrder = null;
    private static int pageSize = -1;//内存分页大小
    static final boolean $assertionsDisabled = !sun/nio/ch/NativeObject.desiredAssertionStatus();
    NativeObject(long l)
    {
        allocationAddress = l;
        address = l;
    }
    NativeObject(long l, long l1)
    {
        allocationAddress = l;
        address = l + l1;
    }
    //分配i大小的内存空间,flag为是否分配内存页
    protected NativeObject(int i, boolean flag)
    {
        if(!flag)
        {
            allocationAddress = unsafe.allocateMemory(i);
            address = allocationAddress;
        } else
        {
            int j = pageSize();
            long l = unsafe.allocateMemory(i + j);
            allocationAddress = l;//已分配内存空间
            address = (l + (long)j) - (l & (long)(j - 1));//空间起始位置
        }
    }
    //获取内存分页大小
    static int pageSize()
    {
        if(pageSize == -1)
            pageSize = unsafe.pageSize();
        return pageSize;
    }
}

//再来看PollArrayWrapper的其他方法
//添加选择key到文件描述包装集合i索引上
void addEntry(int i, SelectionKeyImpl selectionkeyimpl)
{
   //委托给putDescriptor
    putDescriptor(i, selectionkeyimpl.channel.getFDVal());
}

//将文件描述id-j放在索引i上
void putDescriptor(int i, int j)
{
    //委托给pollArray
    pollArray.putInt(SIZE_POLLFD * i + 0, j);
}

//NativeObject
//将文件描述id-j,放在地址i上
final void putInt(int i, int j)
{
    unsafe.putInt((long)i + address, j);
}

存放索引i文件描述信息的兴趣操作事件
 void putEventOps(int i, int j)
    {
        //委托给pollArray
        pollArray.putShort(SIZE_POLLFD * i + 4, (short)j);
    }

//NativeObject
//存放文件描述的兴趣操作事件,放在地址i上
   final void putShort(int i, short word0)
    {
        unsafe.putShort((long)i + address, word0);
    }

//获取索引i的文件描述id
int getDescriptor(int i)
{
    return pollArray.getInt(SIZE_POLLFD * i + 0);
}

//NativeObject
    final short getShort(int i)
    {
        return unsafe.getShort((long)i + address);
    }


//获取索引i的文件描述id关注的兴趣操作事件
int getEventOps(int i)
{
    return pollArray.getShort(SIZE_POLLFD * i + 4);
}

//NativeObject
    final short getShort(int i)
    {
        return unsafe.getShort((long)i + address);
    }


从上面可以好像看出一点门道,PollArrayWrapper作用即存放选择key和选择key关注的
事件,用选择key的文件描述id,表示选择key,文件描述id为int,所以占4个字节,选择key
的兴趣操作事件也为int,即4个字节,所以SIZE_POLLFD为8,文件描述id开始位置FD_OFFSET为0,兴趣事件开始位置EVENT_OFFSET为4;FD_OFFSET和EVENT_OFFSET都是相对于SIZE_POLLFD的。


再来看其他操作
//PollArrayWrapper,替换j索引上的文件描述信息为i索引对应的文件描述信息
void replaceEntry(PollArrayWrapper pollarraywrapper, int i, PollArrayWrapper pollarraywrapper1, int j)
{
    pollarraywrapper1.putDescriptor(j, pollarraywrapper.getDescriptor(i));
    pollarraywrapper1.putEventOps(j, pollarraywrapper.getEventOps(i));
}

添加唤醒管道的source通道文件描述符
void addWakeupSocket(int i, int j)
{
    putDescriptor(j, i);
    //等待唤醒描述符关注的事件是添加事件POLLIN
    putEventOps(j, 1);
}

我猜测一下这个意思,PollArrayWrapper同时存储唤醒等待选择操作的选择器的通道和唤醒通道关注事件即通道注册选择器事件,即添加选择key事件。当有通道注册到选择器,则唤醒通道,唤醒等待选择操作的选择器。
//PollArrayWrapper
//释放内存空间
void free()
{
    pollArray.free();
}

//AllocatedNativeObject
synchronized void free()
{
    if(allocationAddress != 0L)
    {
        unsafe.freeMemory(allocationAddress);
        allocationAddress = 0L;
    }
}

//PollArrayWrapper
//增加i个存储文件描述及相应的兴趣操作事件内存块
void grow(int i)
{
    //重新创建文件描述集合
    PollArrayWrapper pollarraywrapper = new PollArrayWrapper(i);
    //将原始文件描述及相关兴趣操作事件,移到新的集合中
    for(int j = 0; j < size; j++)
        replaceEntry(this, j, pollarraywrapper, j);
    //释放旧集合的空间
    pollArray.free();
    //更新pollArray,容量及起始地址
    pollArray = pollarraywrapper.pollArray;
    size = pollarraywrapper.size;
    pollArrayAddress = pollArray.address();
}

看完这两个集合,再来看WindowsSelectorImpl的构造
WindowsSelectorImpl(SelectorProvider selectorprovider)
        throws IOException
    {
        super(selectorprovider);
	//创建选择器关联通道数组,实际存的为选择key
        channelArray = new SelectionKeyImpl[8];
        totalChannels = 1;
        threadsCount = 0;
        closeLock = new Object();//关闭锁
        interruptTriggered = false;
        updateCount = 0L;
        pollWrapper = new PollArrayWrapper(8);
        wakeupSourceFd = ((SelChImpl)wakeupPipe.source()).getFDVal();//唤醒管道源通道文件描述id
        SinkChannelImpl sinkchannelimpl = (SinkChannelImpl)wakeupPipe.sink();//唤醒管道sink通道
        sinkchannelimpl.sc.socket().setTcpNoDelay(true);//设置唤醒管道sink通道的Socket为无延时
        wakeupSinkFd = sinkchannelimpl.getFDVal();
	//将唤醒管道的源通道文件描述id添加pollWrapper的索引0位置上
        pollWrapper.addWakeupSocket(wakeupSourceFd, 0);
    }

WindowsSelectorImpl默认加载net和nio资源库;WindowsSelectorImpl内锁4个,分别为关闭锁closeLock,中断锁interruptLock,startLock,finishLock后面两个的作用,目前还不清楚,后面再说;一个唤醒管道,作用尚不明确;一个注册到选择器的通道计数器totalChannels;updateCount计数器作用,尚不明确;通道集合channelArray,存放的元素实际为通道关联的选择key;pollWrapper用于存储选择key和相应的兴趣事件,及唤醒管道的源通道,唤醒管道的源通道存放在pollWrapper的索引0位置上。

关于唤醒管道的作用,现在还不是太清楚,在后面的文章中在具体讲解其作用。
我们要关注的几个方法为
1.注册key操作的implRegister方法
2.处理取消key集合方法中implDereg方法
3.选择操作中的doSelect(long l)
4.唤醒方法wakeup
5.实际关闭选择通道方法implClose
由于篇幅问题,这几个方法,放在下一篇文章中再讲

总结:
      WindowsSelectorImpl默认加载net和nio资源库;WindowsSelectorImpl内锁4个,分别为关闭锁closeLock,中断锁interruptLock,startLock,finishLock后面两个的作用,目前还不清楚,后面再说;一个唤醒管道,作用尚不明确;一个注册到选择器的通道计数器totalChannels;updateCount计数器作用,尚不明确;通道集合channelArray,存放的元素实际为通道关联的选择key;pollWrapper用于存储选择key和相应的兴趣事件,及唤醒管道的源通道,唤醒管道的源通道存放在pollWrapper的索引0位置上。
     FdMap主要是存储选择key的,FdMap实际上是一个HashMap,key为选择key的文件描述id,value为MapEntry,MapEntry为选择key的包装Entry,里面含有更新计数器updateCount和清除计数器clearedCount。
     PollArrayWrapper存放选择key和通道及其相关的操作事件。PollArrayWrapper通过AllocatedNativeObject来存储先关的文件描述及其兴趣事件,AllocatedNativeObject
为已分配的底层内存空间,AllocatedNativeObject的内存主要NativeObject来分配,NativeObject实际是通过Unsafe来分配内存。PollArrayWrapper作用即存放选择key和选择key关注的事件,用选择key的文件描述id,表示选择key,文件描述id为int,所以占4个字节,选择key的兴趣操作事件也为int,即4个字节,所以SIZE_POLLFD为8,文件描述id开始位置FD_OFFSET为0,兴趣事件开始位置EVENT_OFFSET为4;FD_OFFSET和EVENT_OFFSET都是相对于SIZE_POLLFD的。PollArrayWrapper同时存储唤醒等待选择操作的选择器的通道和唤醒通道关注事件即通道注册选择器事件,即添加选择key事件。当有通道注册到选择器,则唤醒通道,唤醒等待选择操作的选择器。

WindowsSelectorImpl解析二(选择操作,通道注册,通道反注册,选择器关闭等):
http://donald-draper.iteye.com/blog/2370862
1
1
分享到:
评论
1 楼 ezlhq 2018-07-27  
关于 PollArrayWrapper 状态含义猜测:

参考 SocketChannelImpl#translateAndSetInterestOps:
(http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/db6e25fee0f7/src/share/classes/sun/nio/ch/SocketChannelImpl.java)
 public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
        int newOps = 0;
        if ((ops & SelectionKey.OP_READ) != 0)
            newOps |= PollArrayWrapper.POLLIN;
        if ((ops & SelectionKey.OP_WRITE) != 0)
            newOps |= PollArrayWrapper.POLLOUT;
        if ((ops & SelectionKey.OP_CONNECT) != 0)
            newOps |= PollArrayWrapper.POLLCONN;
        sk.selector.putEventOps(sk, newOps);
    }

可以看出来,POLLIN是read事件,POLLOUT是write事件,POLLCONN是connection事件

参考 ServerSocketChannelImpl#translateAndSetInterestOps:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/1a3de3cdc684/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
        int newOps = 0;

        // Translate ops
        if ((ops & SelectionKey.OP_ACCEPT) != 0)
            newOps |= PollArrayWrapper.POLLIN;
        // Place ops into pollfd array
        sk.selector.putEventOps(sk, newOps);
    }


可以看出来 POLLIN是ACCEPT事件。


其他事件还没看到

相关推荐

    Java-NIO-Netty框架学习

    资源名称:Java-NIO-Netty框架学习资源目录:【】Netty5.0架构剖析和源码解读【】Netty5用户指南【】Netty_in_Action(第五版-目录修正版)【】Netty_in_Action_v08_MEAP【】Netty_in_Action_v10_MEAP【】Netty_代码...

    YOLOv8中加入CBAM注意力机制

    YOLOv8中加入CBAM注意力机制,适合目标检测方向新手小白对YOLOv8作出改进,开箱即用,上传不易,小伙伴拿走的同时请顺手一键三连哈

    高分项目 基于STM32单片机的语音导盲系统设计源代码+原理图+项目资料齐全+教程文档.zip

    【资源概览】 高分项目 基于STM32单片机的语音导盲系统设计源代码+原理图+项目资料齐全+教程文档.zip高分项目 基于STM32单片机的语音导盲系统设计源代码+原理图+项目资料齐全+教程文档.zip高分项目 基于STM32单片机的语音导盲系统设计源代码+原理图+项目资料齐全+教程文档.zip 【资源说明】 高分项目源码:此资源是在校高分项目的完整源代码,经过导师的悉心指导与认可,答辩评审得分高达95分,项目的质量与深度有保障。 测试运行成功:所有的项目代码在上传前都经过了严格的测试,确保在功能上完全符合预期,您可以放心下载并使用。 适用人群广泛:该项目不仅适合计算机相关专业(如电子信息、物联网、通信工程、自动化等)的在校学生和老师,还可以作为毕业设计、课程设计、作业或项目初期立项的演示材料。对于希望进阶学习的小白来说,同样是一个极佳的学习资源。 代码灵活性高:如果您具备一定的编程基础,可以在此代码基础上进行个性化的修改,以实现更多功能。当然,直接用于毕业设计、课程设计或作业也是完全可行的。 欢迎下载,与我一起交流学习,共同进步!

    HTML+CSS+JS精品网页模板H78.rar

    HTML+CSS+JS精品网页模板,设置导航条、轮翻效果,鼠标滑动效果,自动弹窗,点击事件、链接等功能;适用于大学生期末大作业或公司网页的设计制作。响应式网页,可以根据不同的设备屏幕大小自动调整页面布局; 支持如Dreamweaver、HBuilder、Text 、Vscode 等任意html编辑软件进行编辑修改; 支持包括IE、Firefox、Chrome、Safari主流浏览器浏览; 下载文件解压缩,用Dreamweaver、HBuilder、Text 、Vscode 等任意html编辑软件打开,只需更改源代码中的文字和图片可直接使用。图片的命名和格式需要与原图片的名字和格式一致,其他的无需更改。如碰到HTML5+CSS+JS等专业技术问题,以及需要对应行业的模板等相关源码、模板、资料、教程等,随时联系博主咨询。 网页设计和制作、大学生网页课程设计、期末大作业、毕业设计、网页模板,网页成品源代码等,5000+套Web案例源码,主题涵盖各行各业,关注作者联系获取更多源码; 更多优质网页博文、网页模板移步查阅我的CSDN主页:angella.blog.csdn.net。

    PVG800S-800mm皮带检测报告(TABG-ZXJ 2021-069).pdf

    PVG800S-800mm皮带检测报告(TABG-ZXJ 2021-069).pdf

    node-v6.14.1-linux-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    HTML+CSS+JS精品网页模板H56.rar

    HTML+CSS+JS精品网页模板,设置导航条、轮翻效果,鼠标滑动效果,自动弹窗,点击事件、链接等功能;适用于大学生期末大作业或公司网页的设计制作。响应式网页,可以根据不同的设备屏幕大小自动调整页面布局; 支持如Dreamweaver、HBuilder、Text 、Vscode 等任意html编辑软件进行编辑修改; 支持包括IE、Firefox、Chrome、Safari主流浏览器浏览; 下载文件解压缩,用Dreamweaver、HBuilder、Text 、Vscode 等任意html编辑软件打开,只需更改源代码中的文字和图片可直接使用。图片的命名和格式需要与原图片的名字和格式一致,其他的无需更改。如碰到HTML5+CSS+JS等专业技术问题,以及需要对应行业的模板等相关源码、模板、资料、教程等,随时联系博主咨询。 网页设计和制作、大学生网页课程设计、期末大作业、毕业设计、网页模板,网页成品源代码等,5000+套Web案例源码,主题涵盖各行各业,关注作者联系获取更多源码; 更多优质网页博文、网页模板移步查阅我的CSDN主页:angella.blog.csdn.net。

    优秀项目 基于STM32F103单片机的无线测距系统源码+详细文档+全部数据资料.zip

    【资源说明】 优秀项目 基于STM32F103单片机的无线测距系统源码+详细文档+全部数据资料.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(如软件工程、计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    工作总结PPT15.pptx

    工作总结PPT15.pptx

    TortoiseGit安装包和中文语言包(Windows 64-bit)

    TortoiseGit安装包和中文语言包

    氢能行业深度报告之三:质子交换膜PEM市场空间广阔,国产替代进行时.pdf

    氢能行业深度报告之三:质子交换膜PEM市场空间广阔,国产替代进行时

    node-v0.10.45.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    高分项目 基于STM32单片机+openCV的云台色彩追踪系统源代码+项目资料齐全+教程文档.zip

    【资源概览】 高分项目 基于STM32单片机+openCV的云台色彩追踪系统源代码+项目资料齐全+教程文档.zip高分项目 基于STM32单片机+openCV的云台色彩追踪系统源代码+项目资料齐全+教程文档.zip高分项目 基于STM32单片机+openCV的云台色彩追踪系统源代码+项目资料齐全+教程文档.zip 【资源说明】 高分项目源码:此资源是在校高分项目的完整源代码,经过导师的悉心指导与认可,答辩评审得分高达95分,项目的质量与深度有保障。 测试运行成功:所有的项目代码在上传前都经过了严格的测试,确保在功能上完全符合预期,您可以放心下载并使用。 适用人群广泛:该项目不仅适合计算机相关专业(如电子信息、物联网、通信工程、自动化等)的在校学生和老师,还可以作为毕业设计、课程设计、作业或项目初期立项的演示材料。对于希望进阶学习的小白来说,同样是一个极佳的学习资源。 代码灵活性高:如果您具备一定的编程基础,可以在此代码基础上进行个性化的修改,以实现更多功能。当然,直接用于毕业设计、课程设计或作业也是完全可行的。 欢迎下载,与我一起交流学习,共同进步!

    NB-T51004-2012井巷揭煤地质说明书编制规范.pdf

    NB-T51004-2012井巷揭煤地质说明书编制规范.pdf

    工作总结PPT12.pptx

    工作总结PPT12.pptx

    MT T 898-2000 煤炭电法勘探规范.pdf

    MT T 898-2000 煤炭电法勘探规范.pdf

    基于 Python 实现的决策树预测波士顿房价

    决策树 基于 Python 实现的决策树预测波士顿房价

    C# 使用PrintDocument类打印标签

    本文介绍打印机初步配置,以及实现方法。标签主要展示基本信息以及二维码。首先设置打印机纸张大小,纸张高宽度以实际标签为准,设置好后可打印测试页测试一下,以ZDesigner GX430t打印机为例。标签打印C#控制程序源代码,适合自己进行二次开发。软件可以自己编辑标签,可以自动条形码或二维码的位置。标签打印是现代物流和仓储管理中不可或缺的重要环节之一。在许多行业中,如物流、零售和制造业等,标签打印技术被广泛应用于追踪和管理货物、库存和资产。为了满足不同标签打印需求,许多开发者将目光投向了C#编程语言,并且通过自定义控制程序源代码的方式实现标签打印的二次开发。 C#是一种面向对象的高级编程语言,由微软公司开发并广泛应用于Windows系统和.NET框架。它具有强大的功能和灵活的语法结构,使得开发者能够轻松地处理各种复杂的任务。在标签打印的场景下,C#语言提供了丰富的库和工具,使得开发者能够方便地控制打印机、编辑标签布局和生成条形码或二维码等。

    高分项目 基于STM32单片机的智能调速风扇源代码+项目资料齐全+教程文档.zip

    【资源概览】 高分项目 基于STM32单片机的智能调速风扇源代码+项目资料齐全+教程文档.zip高分项目 基于STM32单片机的智能调速风扇源代码+项目资料齐全+教程文档.zip高分项目 基于STM32单片机的智能调速风扇源代码+项目资料齐全+教程文档.zip 【资源说明】 高分项目源码:此资源是在校高分项目的完整源代码,经过导师的悉心指导与认可,答辩评审得分高达95分,项目的质量与深度有保障。 测试运行成功:所有的项目代码在上传前都经过了严格的测试,确保在功能上完全符合预期,您可以放心下载并使用。 适用人群广泛:该项目不仅适合计算机相关专业(如电子信息、物联网、通信工程、自动化等)的在校学生和老师,还可以作为毕业设计、课程设计、作业或项目初期立项的演示材料。对于希望进阶学习的小白来说,同样是一个极佳的学习资源。 代码灵活性高:如果您具备一定的编程基础,可以在此代码基础上进行个性化的修改,以实现更多功能。当然,直接用于毕业设计、课程设计或作业也是完全可行的。 欢迎下载,与我一起交流学习,共同进步!

    2023-06-30-国信证券--储能产业链周评(6月第4周):储能供应商频获海外大单,百亿氢能投资落地鄂尔多斯.pdf

    2023-06-30-国信证券--储能产业链周评(6月第4周):储能供应商频获海外大单,百亿氢能投资落地鄂尔多斯

Global site tag (gtag.js) - Google Analytics