Netty建立服务端,与温湿度传感器进行通信,实时接收设备数据

基于spring boot 框架搭建  quartz做定时发送指令  

pom依赖

    
            org.springframework.boot
            spring-boot-starter-quartz
        

        
        
            org.projectlombok
            lombok
        

        
            io.netty
            netty-all
            4.1.6.Final
        

        
            com.alibaba
            fastjson
            1.2.29
        

netty服务端代码:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TCPServerNetty {
    
    private int port;
    private static Map map = new ConcurrentHashMap();
    private static Map messageMap = new ConcurrentHashMap();
    
    public TCPServerNetty(int port){
        this.port = port;
    }
    
    public TCPServerNetty(){}
    
    public void start() throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(new MyDecoder());
                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 4, 4, -8, 0));
                            ch.pipeline().addLast(new OutBoundHandler());
                            ch.pipeline().addLast( new InBoundHandler());
                        }
                    });
 
           // b.bind(port);
            // Start the server.
            ChannelFuture f = b.bind(port).sync();
 
            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Shut down all event loops to terminate all threads.
            //bossGroup.shutdownGracefully();
            //workerGroup.shutdownGracefully();
        }
    }
    
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String args[]) throws Exception{
        new TCPServerNetty(4456).start();
    }
 
    public static Map getMap() {
        return map;
    }
 
    public static void setMap(Map map) {
        TCPServerNetty.map = map;
    }
 
    public static String bytesToHexString(byte[] src){       
        StringBuilder stringBuilder = new StringBuilder();       
        if (src == null || src.length <= 0) {       
            return null;       
        }       
        for (int i = 0; i < src.length; i++) {       
            int v = src[i] & 0xFF;       
            String hv = Integer.toHexString(v);       
            if (hv.length() < 2) {       
                stringBuilder.append(0);       
            }       
            stringBuilder.append(hv); 
            stringBuilder.append(' ');
        }       
        return stringBuilder.toString();       
    }
 
    /**
     * @return the messageMap
     */
    public static Map getMessageMap() {
        return messageMap;
    }
 
    /**
     * @param messageMap the messageMap to set
     */
    public static void setMessageMap(Map messageMap) {
        TCPServerNetty.messageMap = messageMap;
    }

}
 

解码器:

package com.example.demo.netty2;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.util.List;

/**
 */
public class MyDecoder extends ByteToMessageDecoder{
    
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
        List out) throws Exception {
        byte[] b = new byte[buffer.readableBytes()];
        buffer.readBytes(b);
        out.add(BytesAndHex.bytesTohex(b));
    }
 
    public String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
 
    public static String toHexString1(byte[] b) {
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < b.length; ++i) {
            buffer.append(toHexString1(b[i]));
        }
        return buffer.toString();
    }
 
    public static String toHexString1(byte b) {
        String s = Integer.toHexString(b & 0xFF);
        if (s.length() == 1) {
            return "0" + s;
        } else {
            return s;
        }
    }


}
工具类:

package com.example.demo.netty2;

public class BytesAndHex {
    /**
     * byte[]转十六进制
     * @param bytes
     * @return
     */
    public static String bytesTohex(byte[] bytes) {
        StringBuilder hex = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            boolean flag = false;
            if (b < 0) flag = true;
            int absB = Math.abs(b);
            if (flag) absB = absB | 0x80;
            System.out.println(absB & 0xFF);
            String tmp = Integer.toHexString(absB & 0xFF);
            if (tmp.length() == 1) { //转化的十六进制不足两位,需要补0
                hex.append("0");
            }
            hex.append(tmp.toLowerCase());
        }
        return hex.toString();
    }
 
    /**
     * 十六进制转byte[]
     * @param hex
     * @return
     */
    public static byte[] hexTobytes(String hex) {
        hex = hex.replaceAll(" ", "");
        byte[] bytes = new byte[hex.length() / 2];
        int index = 0;
       while(index < hex.length()){
           String sub = hex.substring(index,index+2);
           bytes[index/2] = (byte)Integer.parseInt(sub,16);
           index += 2;
       }
       return bytes;
    }
}
 

读和写两个服务类:

package com.example.demo.netty2;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class InBoundHandler extends ChannelInboundHandlerAdapter{

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        
        System.out.println("CLIENT"+getRemoteAddress(ctx)+" 接入连接");
        //往channel map中添加channel信息
        TCPServerNetty.getMap().put(getIPString(ctx), ctx.channel());
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //删除Channel Map中的失效Client
        TCPServerNetty.getMap().remove(getIPString(ctx));    
        ctx.close();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg)
            throws Exception {
        System.out.println("来自设备的信息:"+msg);
        
    }
    
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
            throws Exception {
        String socketString = ctx.channel().remoteAddress().toString();
        
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                System.out.println("Client: "+socketString+" READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                System.out.println("Client: "+socketString+" WRITER_IDLE 写超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                System.out.println("Client: "+socketString+" ALL_IDLE 总超时");
                ctx.disconnect();
            }
        }
    }
    public static String getIPString(ChannelHandlerContext ctx){
        String ipString = "";
        String socketString = ctx.channel().remoteAddress().toString();
        int colonAt = socketString.indexOf(":");
        ipString = socketString.substring(1, colonAt);
        return ipString;
    }
    
    
    public static String getRemoteAddress(ChannelHandlerContext ctx){
        String socketString = "";
        socketString = ctx.channel().remoteAddress().toString();
        return socketString;
    }
    
 
    private String getKeyFromArray(byte[] addressDomain) {
        StringBuffer sBuffer = new StringBuffer();
        for(int i=0;i<5;i++){
            sBuffer.append(addressDomain[i]);
        }
        return sBuffer.toString();
    }
 
    protected String to8BitString(String binaryString) {
        int len = binaryString.length();
        for (int i = 0; i < 8-len; i++) {
            binaryString = "0"+binaryString;
        }
        return binaryString;
    }
 
    protected static byte[] combine2Byte(byte[] bt1, byte[] bt2){
        byte[] byteResult = new byte[bt1.length+bt2.length];
        System.arraycopy(bt1, 0, byteResult, 0, bt1.length);
        System.arraycopy(bt2, 0, byteResult, bt1.length, bt2.length);
        return byteResult;
    }
}

package com.example.demo.netty2;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;

public class OutBoundHandler extends ChannelOutboundHandlerAdapter{
private static Logger logger = LoggerFactory.getLogger(OutBoundHandler.class);
    
    @Override
    public void write(ChannelHandlerContext ctx, Object msg,
            ChannelPromise promise) throws Exception {
        
        if (msg instanceof byte[]) {
            byte[] bytesWrite = (byte[])msg;
            ByteBuf buf = ctx.alloc().buffer(bytesWrite.length); 
            logger.info("向设备下发的信息为:"+TCPServerNetty.bytesToHexString(bytesWrite));
        
            buf.writeBytes(bytesWrite); 
            ctx.writeAndFlush(buf).addListener(new ChannelFutureListener(){  
                @Override  
                public void operationComplete(ChannelFuture future)  
                        throws Exception {  
                    logger.info("下发成功!");
                }  
            });
        }
    }
}
 

quartz 定时任务代码:

监听类:

@Slf4j
@Configuration
public class ApplicationStartQuartzJobListener implements ApplicationListener{
    @Autowired
    private QuartzManager quartzManager;
 
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
            quartzManager.startJob();
           System.out.println("任务已经启动......");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
    /**
     * 初始注入scheduler
     */
    @Bean
    public Scheduler scheduler() throws SchedulerException{
        SchedulerFactory schedulerFactoryBean = new StdSchedulerFactory();
        return schedulerFactoryBean.getScheduler();
    }

}

定时任务管理类

@Configuration
public class QuartzManager {
    public static final String JOB1="job1";
    public static final String GROUP1="group1";
    public static final String DEFAULT_CRON="*/5 * * * * ?";
 
    /**
     * 任务调度
     */
    @Autowired
    private Scheduler scheduler;
 
    /**
     * 开始执行定时任务
     */
    public void startJob() throws SchedulerException {
        startJobTask(scheduler);
        scheduler.start();
    }
 
    /**
     * 启动定时任务
     * @param scheduler
     */
    private void startJobTask(Scheduler scheduler) throws SchedulerException {
        JobDetail jobDetail= JobBuilder.newJob(MyJob.class).withIdentity(JOB1,GROUP1).build();
        CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(DEFAULT_CRON);
        CronTrigger cronTrigger=TriggerBuilder.newTrigger().withIdentity(JOB1,GROUP1)
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail,cronTrigger);
 
    }
    /**
     * 获取Job信息
     * @param name
     * @param group
     */
    public String getjobInfo(String name,String group) throws SchedulerException {
        TriggerKey triggerKey=new TriggerKey(name,group);
        CronTrigger cronTrigger= (CronTrigger) scheduler.getTrigger(triggerKey);
        return String.format("time:%s,state:%s",cronTrigger.getCronExpression(),
                scheduler.getTriggerState(triggerKey).name());
    }
 
    /**
     * 修改任务的执行时间
     * @param name
     * @param group
     * @param cron cron表达式
     * @return
     * @throws SchedulerException
     */
    public boolean modifyJob(String name,String group,String cron) throws SchedulerException{
        Date date=null;
        TriggerKey triggerKey=new TriggerKey(name, group);
        CronTrigger cronTrigger= (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldTime=cronTrigger.getCronExpression();
        if (!oldTime.equalsIgnoreCase(cron)){
            CronScheduleBuilder cronScheduleBuilder=CronScheduleBuilder.cronSchedule(cron);
            CronTrigger trigger=TriggerBuilder.newTrigger().withIdentity(name,group)
                    .withSchedule(cronScheduleBuilder).build();
            date=scheduler.rescheduleJob(triggerKey,trigger);
        }
        return date !=null;
    }
 
    /**
     * 暂停所有任务
     * @throws SchedulerException
     */
    public void pauseAllJob()throws SchedulerException{
        scheduler.pauseAll();
    }
 
    /**
     * 暂停某个任务
     * @param name
     * @param group
     * @throws SchedulerException
     */
    public void pauseJob(String name,String group)throws SchedulerException{
        JobKey jobKey=new JobKey(name,group);
        JobDetail jobDetail=scheduler.getJobDetail(jobKey);
        if (jobDetail==null)
            return;
        scheduler.pauseJob(jobKey);
    }
 
    /**
     * 恢复所有任务
     * @throws SchedulerException
     */
    public void resumeAllJob()throws SchedulerException{
        scheduler.resumeAll();
    }
    /**
     * 恢复某个任务
     */
    public void resumeJob(String name,String group)throws SchedulerException{
        JobKey jobKey=new JobKey(name,group);
        JobDetail jobDetail=scheduler.getJobDetail(jobKey);
        if (jobDetail==null)
            return;
        scheduler.resumeJob(jobKey);
    }
 
    /**
     * 删除某个任务
     * @param name
     * @param group
     * @throws SchedulerException
     */
    public void deleteJob(String name,String group)throws SchedulerException{
        JobKey jobKey=new JobKey(name, group);
        JobDetail jobDetail=scheduler.getJobDetail(jobKey);
        if (jobDetail==null)
            return;
        scheduler.deleteJob(jobKey);
    }
}

任务工厂

@Component
public class MyJobFactory extends AdaptableJobFactory{
    
     @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;
     
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            // 调用父类的方法
            Object jobInstance = super.createJobInstance(bundle);
            // 进行注入
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }

}

具体任务类:此处是拿到接入设备的通道,,发送指令

public class MyJob implements Job{
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            executeTask();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
 
    private void executeTask() throws SchedulerException {
        //String ip = "169.254.249.123";
        String ip = "169.254.21.39";
        Map map = TCPServerNetty.getMap();
        if(map.get(ip) != null){
            Channel ch = map.get(ip);
            String content = "01 03 00 00 00 02 C4 0B";
            byte[] bs = BytesAndHex.hexTobytes(content);
            ch.writeAndFlush(bs);
        }
    }
}

spring boot启动

@SpringBootApplication
@EntityScan(basePackages={"com.example.*"})
public class DemoApplication implements CommandLineRunner{
    
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        new TCPServerNetty(4455).start();
    }
    
}

最终的效果为:

2019-08-26 13:33:10.772  INFO 11712 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler 'quartzScheduler' initialized from an externally provided properties instance.
2019-08-26 13:33:10.772  INFO 11712 --- [           main] org.quartz.impl.StdSchedulerFactory      : Quartz scheduler version: 2.3.1
2019-08-26 13:33:10.772  INFO 11712 --- [           main] org.quartz.core.QuartzScheduler          : JobFactory set to: org.springframework.scheduling.quartz.SpringBeanJobFactory@42f9c19a
2019-08-26 13:33:11.086  INFO 11712 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2019-08-26 13:33:11.149  INFO 11712 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2019-08-26 13:33:11.165  INFO 11712 --- [           main] o.s.i.channel.PublishSubscribeChannel    : Channel 'application.errorChannel' has 1 subscriber(s).
2019-08-26 13:33:11.165  INFO 11712 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean '_org.springframework.integration.errorLogger'
2019-08-26 13:33:11.165  INFO 11712 --- [           main] o.s.s.quartz.SchedulerFactoryBean        : Starting Quartz Scheduler now
2019-08-26 13:33:11.165  INFO 11712 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler quartzScheduler_$_NON_CLUSTERED started.
2019-08-26 13:33:11.180  INFO 11712 --- [           main] org.quartz.core.QuartzScheduler          : Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
任务已经启动......
2019-08-26 13:33:11.180  INFO 11712 --- [           main] com.example.DemoApplication              : Started DemoApplication in 1.567 seconds (JVM running for 2.115)
2019-08-26 13:33:11.666  INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0x8339289a] REGISTERED
2019-08-26 13:33:11.675  INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0x8339289a] BIND: 0.0.0.0/0.0.0.0:4455
2019-08-26 13:33:11.676  INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0x8339289a, L:/0:0:0:0:0:0:0:0:4455] ACTIVE
2019-08-26 13:33:11.922  INFO 11712 --- [ntLoopGroup-4-1] io.netty.handler.logging.LoggingHandler  : [id: 0x8339289a, L:/0:0:0:0:0:0:0:0:4455] RECEIVED: [id: 0x03de23cd, L:/169.254.249.123:4455 - R:/169.254.21.39:23]
CLIENT/169.254.21.39:23 接入连接
2019-08-26 13:33:15.065  INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler  : 向设备下发的信息为:01 03 00 00 00 02 c4 0b 
2019-08-26 13:33:15.073  INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler  : 下发成功!
来自设备的信息:0103040192010d5bd1
2019-08-26 13:33:20.006  INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler  : 向设备下发的信息为:01 03 00 00 00 02 c4 0b 
2019-08-26 13:33:20.010  INFO 11712 --- [ntLoopGroup-5-1] com.example.demo.netty2.OutBoundHandler  : 下发成功!
来自设备的信息:0103040191010d0a6f
文章中定时任务与netty代码融合其他文章的。。我这边只是把它实现和硬件设备实时通讯。。留个纪念

 

 

 

 

 

你可能感兴趣的:(Netty建立服务端,与温湿度传感器进行通信,实时接收设备数据)