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

netty NioServerSocketChannel解析

阅读更多
netty ServerBootStrap解析:http://donald-draper.iteye.com/blog/2392572
netty 通道接口定义:http://donald-draper.iteye.com/blog/2392740
netty 抽象通道初始化:http://donald-draper.iteye.com/blog/2392801
netty 抽象Unsafe定义:http://donald-draper.iteye.com/blog/2393053
netty 通道Outbound缓冲区:http://donald-draper.iteye.com/blog/2393098
netty 抽象通道后续:http://donald-draper.iteye.com/blog/2393166
netty 抽象nio通道:http://donald-draper.iteye.com/blog/2393269
netty 抽象nio字节通道:http://donald-draper.iteye.com/blog/2393323
netty 抽象nio消息通道:http://donald-draper.iteye.com/blog/2393364
引言
上一篇我们看了抽象nio消息通道,先来回顾一下:
抽象Nio消息通道AbstractNioMessageChannel,写通道Outbound缓冲区消息,即遍历通道Outbound缓冲区刷新链,当写消息请求为空时,从选择key兴趣集中移除写操作事件,否则,委托doWriteMessage方法,将消息写到底层通道,doWriteMessage方法待子类扩展,写完,将写请求从刷新链上移除,否则,如果需要,添加写事件到选择key的兴趣事件集。

nio消息Unsafe(NioMessageUnsafe)读操作,从通道接收缓冲区读取数据,通知通道处理读取数据,触发Channel管道线的fireChannelRead事件,待数据读取完毕,触发Channel管道线的fireChannelReadComplete事件,如果在读数据的过程中,通道关闭,则触发通道输入关闭事件(fireUserEventTriggered),如果在读数据的过程中,发生异常,则触发通道fireExceptionCaught事件,如果读任务完毕,且不需自动读,则从选择key兴趣事件集移除读操作事件

今天终于到我们的目的了nio 服务端socket通道,NioServerSocketChannel,
package io.netty.channel.socket.nio;

import io.netty.channel.ChannelException;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.util.internal.SocketUtils;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
import io.netty.channel.socket.ServerSocketChannelConfig;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.List;

/**
 * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses
 * NIO selector based implementation to accept new connections.
 */
public class NioServerSocketChannel extends AbstractNioMessageChannel
                             implements io.netty.channel.socket.ServerSocketChannel {
    private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);//通道元数据
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();//选择器提供者
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
    private final ServerSocketChannelConfig config;//通道配置
}

从上面来看,nio服务端socket通道内部有两个变量,一个为选择器提供者,一个为通道配置。
来看构造
/**
 * Create a new instance
 */
public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

/**
 * Create a new instance using the given {@link SelectorProvider}.
 */
public NioServerSocketChannel(SelectorProvider provider) {
    this(newSocket(provider));
}

/**
 * Create a new instance using the given {@link ServerSocketChannel}.
 */
public NioServerSocketChannel(ServerSocketChannel channel) {
    super(null, channel, SelectionKey.OP_ACCEPT);
    //创建通道配置
    config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}


来看创建socket通道,
private static ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        /**
         *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
         *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
         *
         *  See [url=https://github.com/netty/netty/issues/2308]#2308[/url].
	 委托给选择器提供者,打开一个通道
         */
        return provider.openServerSocketChannel();
    } catch (IOException e) {
        throw new ChannelException(
                "Failed to open a server socket.", e);
    }
}

关于通道配置NioServerSocketChannelConfig,我们nio服务端socket通道的内部方法看完,再来看
回头看通道配置,


来看其他方法:

//获取本地socket地址
@Override
public InetSocketAddress localAddress() {
    return (InetSocketAddress) super.localAddress();
}
//获取通道元数据
@Override
public ChannelMetadata metadata() {
    return METADATA;
}
//获取通道配置
@Override
public ServerSocketChannelConfig config() {
    return config;
}
//判断通道,是否激活,主要通过通道关联socket的isBound方法判断
@Override
public boolean isActive() {
    return javaChannel().socket().isBound();
}
//获取关联socket通道
@Override
protected ServerSocketChannel javaChannel() {
    return (ServerSocketChannel) super.javaChannel();
}
//远端地址为空
@Override
public InetSocketAddress remoteAddress() {
    return null;
}
//安全获取本地socket地址
@Override
protected SocketAddress localAddress0() {
    return SocketUtils.localSocketAddress(javaChannel().socket());
}
//关闭通道
@Override
protected void doClose() throws Exception {
    javaChannel().close();
}

//绑定socket地址
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        //如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        //否则使用通道关联Socket的bind方法,绑定socket地址
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

从上面来看,通道实际绑定socket地址,首先判断jdk版本信息,如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址,
否则为通道关联Socket的bind方法。

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    //接受通道连接,并创建与客户端交互的socket通道
    SocketChannel ch = SocketUtils.accept(javaChannel());

    try {
        if (ch != null) {
	    //将创建的与客户端交互的socket通道,添加到结果集
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    } catch (Throwable t) {
        logger.warn("Failed to create a new channel from an accepted socket.", t);

        try {
            ch.close();
        } catch (Throwable t2) {
            logger.warn("Failed to close a socket.", t2);
        }
    }

    return 0;
}
//SocketUtils
//安全接受socket连接
 public static SocketChannel accept(final ServerSocketChannel serverSocketChannel) throws IOException {
    try {
        return AccessController.doPrivileged(new PrivilegedExceptionAction<SocketChannel>() {
            @Override
            public SocketChannel run() throws IOException {
                return serverSocketChannel.accept();
            }
        });
    } catch (PrivilegedActionException e) {
        throw (IOException) e.getCause();
    }
}


读取的消息委托给谁来处理呢,这要回到SeverBootStrap这篇文章,主要是ServerBootstrapAcceptor。
netty ServerBootStrap解析:http://donald-draper.iteye.com/blog/2392572

下面我们来看简单说一下,从ServerBootStrap初始化通道开始:

下面我们来看初始化通道,这个是重点:
//SeverBootStrap
@Override  
void init(Channel channel) throws Exception {  
    final Map<ChannelOption<?>, Object> options = options0();  
    synchronized (options) {  
        //设置父Server通道选项  
        setChannelOptions(channel, options, logger);  
    }  
  
    final Map<AttributeKey<?>, Object> attrs = attrs0();  
    synchronized (attrs) {  
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {  
            @SuppressWarnings("unchecked")  
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();  
            //设置父Server通道属性  
            channel.attr(key).set(e.getValue());  
        }  
    }  
   //获取Server通道的Channel管道  
    ChannelPipeline p = channel.pipeline();  
    final EventLoopGroup currentChildGroup = childGroup;  
    final ChannelHandler currentChildHandler = childHandler;  
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;  
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;  
  
    synchronized (childOptions) {  
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));  
    }  
  
    synchronized (childAttrs) {  
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));  
    }  
  
    p.addLast(new ChannelInitializer<Channel>() {  
        @Override  
        public void initChannel(final Channel ch) throws Exception {  
            final ChannelPipeline pipeline = ch.pipeline();  
            ChannelHandler handler = config.handler();  
            if (handler != null) {  
            //将通道处理器添加到通道内部的Channel管道内  
                pipeline.addLast(handler);  
            }  
            ch.eventLoop().execute(new Runnable() {  
                @Override  
                public void run() {  
            //将Server引导配置监听器添加到通道内部的Channel管道内 ,这个是重点
                    pipeline.addLast(new ServerBootstrapAcceptor(  
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));  
                }  
            });  
        }  
    });  
}  

我们来看引导配置监听器,实际为一个Inbound通道处理器

  
 private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {  
        private final EventLoopGroup childGroup;//与客户端交互通道注册的事件循环组  
        private final ChannelHandler childHandler;//与客户端交互通道的通道处理器  
        private final Entry<ChannelOption<?>, Object>[] childOptions;//与客户端交互通道的选项配置  
        private final Entry<AttributeKey<?>, Object>[] childAttrs;//与客户端交互通道的属性  
        private final Runnable enableAutoReadTask;  
      
        ServerBootstrapAcceptor(  
                final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,  
                Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {  
            this.childGroup = childGroup;  
            this.childHandler = childHandler;  
            this.childOptions = childOptions;  
            this.childAttrs = childAttrs;  
      
            // Task which is scheduled to re-enable auto-read.  
            // It's important to create this Runnable before we try to submit it as otherwise the URLClassLoader may  
            // not be able to load the class because of the file limit it already reached.  
            // 此任务用于开启通道自动读取配置,将会被所在的事件循环调度。  
            // See https://github.com/netty/netty/issues/1328  
            enableAutoReadTask = new Runnable() {  
                @Override  
                public void run() {  
             //开启通道自动读取配置  
                    channel.config().setAutoRead(true);  
                }  
            };  
        }  
       //通道读取操作
        @Override  
        @SuppressWarnings("unchecked")  
        public void channelRead(ChannelHandlerContext ctx, Object msg) {  
            //与客户端交互通道 ,这个就是在nio服务端socket通道中,doReadMessages方法接受客户端连接,
	    //创建的客户端交互socket通道
            final Channel child = (Channel) msg;  
            //配置与客户端交互通道的通道处理器  
            child.pipeline().addLast(childHandler);  
            //配置与客户端交互通道的选项  
            setChannelOptions(child, childOptions, logger);  
            for (Entry<AttributeKey<?>, Object> e: childAttrs) {  
        //配置与客户端交互通道的属性  
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());  
            }  
      
            try {  
        //注册与客户端交互通道到childGroup事件循环组  
                childGroup.register(child).addListener(new ChannelFutureListener() {  
                    @Override  
                    public void operationComplete(ChannelFuture future) throws Exception {  
                        if (!future.isSuccess()) {  
                 //注册失败,则关闭通道  
                            forceClose(child, future.cause());  
                        }  
                    }  
                });  
            } catch (Throwable t) {  
                forceClose(child, t);  
            }  
        }  
         //关闭通道  
        private static void forceClose(Channel child, Throwable t) {  
            child.unsafe().closeForcibly();  
            logger.warn("Failed to register an accepted channel: {}", child, t);  
        }  
      
        @Override  
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
            final ChannelConfig config = ctx.channel().config();  
            if (config.isAutoRead()) {  
                // stop accept new connections for 1 second to allow the channel to recover  
         //发生异常,则停止接受连接请求1秒钟,允许通道恢复  
                // See https://github.com/netty/netty/issues/1328  
                config.setAutoRead(false);  
                ctx.channel().eventLoop().schedule(enableAutoReadTask, 1, TimeUnit.SECONDS);  
            }  
            // still let the exceptionCaught event flow through the pipeline to give the user  
            // a chance to do something with it  
     //触发异常  
            ctx.fireExceptionCaught(cause);  
        }  
    }  


从上面来看,doReadMessages方法,实际为当接受客户端的连接请求时,创建一个与客户端交互的socket通道,并添加到读操作结果集中,实际为socket通道集。并将socket通道集交给ServerBootStrap的引导配置监听器ServerBootstrapAcceptor处理,Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。 客户端连接服务端时,首先向服务端发送连接请求数据,服务端接受到连接请求时,创建一个与客户端交互的socket通道。

再来看其他方法
// Unnecessary stuff
//由于服务端通道用于接受客户端的请求,所有不支持连接,写消息,消息过滤等等操作
@Override
protected boolean doConnect(
        SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected void doFinishConnect() throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected SocketAddress remoteAddress0() {
    return null;
}

@Override
protected void doDisconnect() throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected boolean doWriteMessage(Object msg, ChannelOutboundBuffer in) throws Exception {
    throw new UnsupportedOperationException();
}

@Override
protected final Object filterOutboundMessage(Object msg) throws Exception {
    throw new UnsupportedOperationException();
}

我们再回到Nio服务端通道配置,
//Nio服务端通道配置, 为NioServerSocketChannel的内部类,这个我们单独列一篇文章来说
private final class NioServerSocketChannelConfig extends DefaultServerSocketChannelConfig {
    private NioServerSocketChannelConfig(NioServerSocketChannel channel, ServerSocket javaSocket) {
        super(channel, javaSocket);
    }

    @Override
    protected void autoReadCleared() {
        clearReadPending();
    }
}


总结:

nio服务端socket通道NioServerSocketChannel内部有两个变量,一个为选择器提供者SelectorProvider,一个为通道配置ServerSocketChannelConfig。

通道实际绑定socket地址,首先判断jdk版本信息,如果jdk版本大于1.7 则使用通道bind方法,绑定socket地址,否则为通道关联Socket的bind方法。

doReadMessages方法,实际为当接受客户端的连接请求时,创建一个与客户端交互的socket通道,并添加到读操作结果集中,实际为socket通道集。并将socket通道集交给ServerBootStrap的引导配置监听器ServerBootstrapAcceptor处理,Server引导配置监听器实际为一个Inbound通道处理器,每当有客户端连接请求时,则创建一个与客户端交互的通道,将child通道选项及属性配置给通道,并将通道注册到childGroup事件循环组,然后将通道处理器添加到与客户端交互的通道内部的Channel管道中。 客户端连接服务端时,首先向服务端发送连接请求数据,服务端接受到连接请求时,创建一个与客户端交互的socket通道。

由于服务端通道用于接受客户端的请求,所有不支持连接,写消息,消息过滤等等操作。

0
1
分享到:
评论

相关推荐

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    智能点阵笔项目源代码全套技术资料.zip

    智能点阵笔项目源代码全套技术资料.zip

    英文字母手语图像分类数据集【已标注,约26,000张数据】

    英文字母手语图像分类数据集【已标注,约26,000张数据】 分类个数【28】:a、b、c等【具体查看json文件】 划分了训练集、测试集。存放各自的同一类数据图片。如果想可视化数据集,可以运行资源中的show脚本。 CNN分类网络改进:https://blog.csdn.net/qq_44886601/category_12858320.html 【更多图像分类、图像分割(医学)、目标检测(yolo)的项目以及相应网络的改进,可以参考本人主页:https://blog.csdn.net/qq_44886601/category_12803200.html】

    (31687028)PID控制器matlab仿真.zip

    标题中的“PID控制器matlab仿真.zip”指的是一个包含PID控制器在MATLAB环境下进行仿真的资源包。PID(比例-积分-微分)控制器是一种广泛应用的自动控制算法,它通过结合当前误差、过去误差的积分和误差变化率的微分来调整系统输出,以达到期望的控制效果。MATLAB是一款强大的数学计算软件,而Simulink是MATLAB的一个扩展模块,专门用于建模和仿真复杂的动态系统。 描述中提到,“PID控制器——MATLAB/Simulink仿真以及性能比较与分析”表明这个资源包不仅提供了PID控制器的模型,还可能包括对不同参数配置下的性能比较和分析。博主分享的是“最新升级版框架的Simulink文件”,意味着这些文件基于最新的MATLAB版本进行了优化,确保了与不同版本的MATLAB(从2015a到2020a共11个版本)的兼容性,这为用户提供了广泛的应用范围。 标签中的“PID”、“matlab”、“simulink”、“博文附件”和“多版本适用”进一步细化了内容的关键点。这表示该资源包是博客文章的附加材料,专门针对PID控制器在MATLAB的Simulink环境中进行仿真实验。多

    MATLAB代码:考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型 关键词:碳捕集 综合能源系统 电转气P2G 热电联产 低碳调度 参考文档:Modeling and Optimiza

    MATLAB代码:考虑P2G和碳捕集设备的热电联供综合能源系统优化调度模型 关键词:碳捕集 综合能源系统 电转气P2G 热电联产 低碳调度 参考文档:《Modeling and Optimization of Combined Heat and Power with Power-to-Gas and Carbon Capture System in Integrated Energy System》完美复现 仿真平台:MATLAB yalmip+gurobi 主要内容:代码主要做的是一个考虑电转气P2G和碳捕集设备的热电联供综合能源系统优化调度模型,模型耦合CHP热电联产单元、电转气单元以及碳捕集单元,并重点考虑了碳交易机制,建立了综合能源系统运行优化模型,模型为非线性模型,采用yalmip加ipopt对其进行高效求解,该模型还考虑了碳排放和碳交易,是学习低碳经济调度必备程序 代码非常精品,注释保姆级 这段代码是一个用于能源系统中的综合能源系统(Integrated Energy System)建模和优化的程序。它使用了MATLAB的优化工具箱和SDP(半定规划)变量来定义决策变

    中国飞行器设计大赛圆筒权重文件

    中国飞行器设计大赛圆筒权重文件

    java毕设项目之ssm社区文化宣传网站+jsp(完整前后端+说明文档+mysql+lw).zip

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

    风光储、风光储并网直流微电网simulink仿真模型 系统由光伏发电系统、风力发电系统、混合储能系统(可单独储能系统)、逆变器VSR+大电网构成 光伏系统采用扰动观察法实现mppt控

    风光储、风光储并网直流微电网simulink仿真模型。 系统由光伏发电系统、风力发电系统、混合储能系统(可单独储能系统)、逆变器VSR+大电网构成。 光伏系统采用扰动观察法实现mppt控制,经过boost电路并入母线; 风机采用最佳叶尖速比实现mppt控制,风力发电系统中pmsg采用零d轴控制实现功率输出,通过三相电压型pwm变器整流并入母线; 混合储能由蓄电池和超级电容构成,通过双向DCDC变器并入母线,并采用低通滤波器实现功率分配,超级电容响应高频功率分量,蓄电池响应低频功率分量,有限抑制系统中功率波动,且符合储能的各自特性。 并网逆变器VSR采用PQ控制实现功率入网 以下是视频讲解文案: 接下来我来介绍一下 就是这个风光储直流微电网 整个仿真系统的一些架构啊 然后按照需求呢正常的讲一些 多讲一些 就是储能的这块的 还有这个并网的 三相两电瓶调的这个 并网继变器的这个模块 首先就是来介绍一下呃 整个系统的一个架构 你可以看到这个系统的架构 分别有四大部分组成 最左边的这块就是混合储能啊 这边这个是蓄电池 这个超级电容 他们都是

    ajax发请求示例.txt

    ajax发请求示例.txt

    深圳建筑安装公司“电工安全技术操作规程”.docx

    深圳建筑安装公司“电工安全技术操作规程”

    220) Vinkmag - 多概念创意报纸新闻杂志 WordPress v5.0.zip

    220) Vinkmag - 多概念创意报纸新闻杂志 WordPress v5.0.zip

    智力残疾评定标准一览表.docx

    智力残疾评定标准一览表.docx

    MDIN380 SDI转VGA 转LVDS VGA转SDI 高清视频处理 MDIN380芯片 PCB代码方案资料 3G-SDI转VGA ?3G-SDI转LVDS ?高清视频 MDIN380、GV76

    MDIN380 SDI转VGA 转LVDS VGA转SDI 高清视频处理 MDIN380芯片 PCB代码方案资料 3G-SDI转VGA ?3G-SDI转LVDS ?高清视频 MDIN380、GV7601 芯片方案(PCB图和源码)。 此方案是韩国视频处理芯片MDIN380的整合应用方案。 3G-SDI转VGA或3G-SDI转LVDS。 方案共有两块电路板(一块底板,一块MDIN380核心板 四层板)。 MDIN380和GV7601 都是BGA封装,最好有焊接BGA经验才拿。 另外有视频处理方面其它需要可联系我定制开发。 其它视频格式转,视频图像分割、拼接等可定制开发。 方案资料含有源码、PCB图。 方案已有成熟产品在应用。 注意该资料没有原理图,只有PCB图。 代码环境编译KEIL4。 画图软件Protel99、AD10。 电子文档资料

    YOLO算法-锡罐-牙罐-盖子打开数据集-179张图像带标签-锡罐-牙罐-盖子打开.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> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    G120 EPOS基本定位功能关键点系列-堆垛机报F7452追踪原因.mp4

    G120 EPOS基本定位功能关键点系列_堆垛机报F7452追踪原因.mp4

    java毕设项目之ssm亚盛汽车配件销售业绩管理统+jsp(完整前后端+说明文档+mysql+lw).zip

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

    zigbee CC2530无线自组网协议栈系统代码实现协调器与终端基于GenericApp的无线收发例程.zip

    1、嵌入式物联网单片机项目开发例程,简单、方便、好用,节省开发时间。 2、代码使用IAR软件开发,当前在CC2530上运行,如果是其他型号芯片,请自行移植。 3、软件下载时,请注意接上硬件,并确认烧录器连接正常。 4、有偿指导v:wulianjishu666; 5、如果接入其他传感器,请查看账号发布的其他资料。 6、单片机与模块的接线,在代码当中均有定义,请自行对照。 7、若硬件有差异,请根据自身情况调整代码,程序仅供参考学习。 8、代码有注释说明,请耐心阅读。 9、例程具有一定专业性,非专业人士请谨慎操作。

    基于小程序的小区物业新冠疫情物资管理平台小程序源代码(java+小程序+mysql+LW).zip

    系统可以提供信息显示和相应服务,其管理小区物业新冠疫情物资管理平台信息,查看小区物业新冠疫情物资管理平台信息,管理小区物业新冠疫情物资管理平台。 项目包含完整前后端源码和数据库文件 环境说明: 开发语言:Java JDK版本:JDK1.8 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/idea Maven包:Maven3.3 部署容器:tomcat7 小程序开发工具:hbuildx/微信开发者工具

    亲测源码云赏V7.0微信视频打赏系统源码已测试完整无错版

    云赏V7.0包括V6的所有功能外,全新UI设计,代理可以选择8种风格,添加后台统计等多种功能。 1基本设置(网站基础信息配置、包括主域名、防封尾缀、url.cnt.cn短连接接口可切换); 2转跳域名(10层防守转跳,都输入的话,都会转跳到对应的地方在跳回来,在随机取用落地); 3落地域名(添加落地域名及设置默认落地域名); 4视频列表(添加视频批量添加外链视频给代理们获取); 5代理推广:代理使用推广链接发展下级代理,后台设置提成); 6代理列表(生成邀请码注册,手动添加代理); 7提现记录(用于结算代理们的提现); 8余额记录(记录代理的余额变动); 9订单记录(记录打赏数,今日收入)。 测试环境: Nginx 1.18+PHP56+MySQL5.6,详细教程见文件内文字教程。 后台账号:admin 密码:admin888

    深圳建设施工项目易燃、易爆、有毒、有害物品管理制度.docx

    深圳建设施工项目易燃、易爆、有毒、有害物品管理制度

Global site tag (gtag.js) - Google Analytics