基于 Netty + RXTX 的无协议 COM 通讯案例实现

参考

Netty集成串口RXTX编程,为什么过时了?

Java版本

java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

RXTX版本

# 官网 http://rxtx.qbang.org/wiki/index.php/Download
# 版本 rxtx-2.2pre1-bins

POM依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>
    </dependencies>

Com 配置类封装

java 复制代码
package com.yushanma.config;

import io.netty.channel.rxtx.RxtxChannelConfig;
import lombok.Data;

/**
 * desc
 *
 * @author Yushanma
 * @since 2023/6/4 17:20
 */
@Data
public final class ComConfig {
    /**
     * 串口名称,以COM开头(COM0、COM1、COM2等等)
     */
    private String serialPortName;
    /**
     * 波特率, 默认:9600
     */
    private int baudRate = 9600;
    /**
     * 数据位 默认8位
     * 可以设置的值:SerialPort.DATABITS_5、SerialPort.DATABITS_6、SerialPort.DATABITS_7、SerialPort.DATABITS_8
     */
    private RxtxChannelConfig.Databits dataBits = RxtxChannelConfig.Databits.DATABITS_8;
    /**
     * 停止位
     * 可以设置的值:SerialPort.STOPBITS_1、SerialPort.STOPBITS_2、SerialPort.STOPBITS_1_5
     */
    private RxtxChannelConfig.Stopbits stopBits = RxtxChannelConfig.Stopbits.STOPBITS_1;
    /**
     * 校验位
     * 可以设置的值:SerialPort.PARITY_NONE、SerialPort.PARITY_ODD、SerialPort.PARITY_EVEN、SerialPort.PARITY_MARK、SerialPort.PARITY_SPACE
     */
    private RxtxChannelConfig.Paritybit parity = RxtxChannelConfig.Paritybit.NONE;
}

字节编解码工具

java 复制代码
package com.yushanma.utils;

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

/**
 * 解码器
 *
 * @author Yushanma
 * @since 2023/6/4 17:17
 */
public class ByteArrayDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 标记一下当前的 readIndex 的位置
        in.markReaderIndex();
        int dataLength = in.readableBytes();
        byte[] array = new byte[dataLength];
        in.readBytes(array, 0, dataLength);
        if(array.length > 0){
            out.add(array);
        }
    }
}
java 复制代码
package com.yushanma.utils;

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

/**
 * 编码器
 *
 * @author Yushanma
 * @since 2023/6/4 17:18
 */
public class ByteArrayEncoder extends MessageToByteEncoder<byte[]> {

    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf out) throws Exception {
        // 消息体,包含我们要发送的数据
        out.writeBytes(msg);
    }
}

Channel 事件处理器

java 复制代码
package com.yushanma.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;

import java.time.LocalDateTime;

/**
 * Channel 事件处理器
 * 需要处理什么事件就重写 SimpleChannelInboundHandler 中对应的方法
 * @author Yushanma
 * @since 2023/6/4 17:19
 */
@ChannelHandler.Sharable
public class RxtxHandler extends SimpleChannelInboundHandler<byte[]> {

    /**
     * 当 COM 口接收到数据时
     * 根据不同的进制对数据包进行解码
     * 然后执行对应业务逻辑
     * @param ctx 上下文
     * @param msg 接收到的数据包
     * @throws Exception 异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, byte[] msg) throws Exception {

        // 十六进制发送编解码
        int dataLength = msg.length;
        ByteBuf buf = Unpooled.buffer(dataLength);
        buf.writeBytes(msg);
        System.out.println(LocalDateTime.now() + "接收到:");
        while(buf.isReadable()){
            System.out.print(buf.readByte() + " ");
        }
        System.out.println();
        // 释放资源
        ReferenceCountUtil.release(msg);
    }
}

启动类封装

java 复制代码
package com.yushanma.server;

import com.yushanma.config.ComConfig;
import com.yushanma.handler.RxtxHandler;
import com.yushanma.utils.ByteArrayDecoder;
import com.yushanma.utils.ByteArrayEncoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.rxtx.RxtxChannel;
import io.netty.channel.rxtx.RxtxDeviceAddress;
import io.netty.util.concurrent.GenericFutureListener;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

/**
 * desc
 *
 * @author Yushanma
 * @since 2023/6/4 17:23
 */
public class RxtxServer {

    private RxtxChannel channel;

    private ComConfig config;

    public RxtxServer(ComConfig config) {
        this.config = config;
    }

    public void createRxtx() throws Exception {
        // 串口使用阻塞io
        EventLoopGroup group = new OioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channelFactory(() -> {
                        RxtxChannel rxtxChannel = new RxtxChannel();
                        rxtxChannel.config()
                                .setBaudrate(config.getBaudRate()) // 波特率
                                .setDatabits(config.getDataBits()) // 数据位
                                .setParitybit(config.getParity())    // 校验位
                                .setStopbits(config.getStopBits()); // 停止位
                        return rxtxChannel;
                    })
                    .handler(new ChannelInitializer<RxtxChannel>() {
                        @Override
                        protected void initChannel(RxtxChannel rxtxChannel) {
                            rxtxChannel.pipeline().addLast(
                                    // 十六进制形式发送编解码
                                    new ByteArrayDecoder(),
                                    new ByteArrayEncoder(),
                                    new RxtxHandler()
                            );
                        }
                    });

            ChannelFuture f = bootstrap.connect(new RxtxDeviceAddress(config.getSerialPortName())).sync();
            f.addListener(connectedListener);

            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }


    // 连接监听
    GenericFutureListener<ChannelFuture> connectedListener = (ChannelFuture f) -> {
        final EventLoop eventLoop = f.channel().eventLoop();
        if (!f.isSuccess()) {
            System.out.println("连接失败");
        } else {
            channel = (RxtxChannel) f.channel();
            System.out.println("连接成功");
            sendData();
        }
    };

    /**
     * 发送数据
     */
    public void sendData() {
        // 十六机制形式发送
        ByteBuf buf = Unpooled.buffer(2);
        buf.writeByte(3);
        buf.writeByte(2);
        channel.writeAndFlush(buf.array());

        // 文本形式发送
        //channel.writeAndFlush("2");
    }

    public void start() {
        CompletableFuture.runAsync(() -> {
            try {
                // 阻塞的函数
                createRxtx();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, Executors.newSingleThreadExecutor());//不传默认使用ForkJoinPool,都是守护线程
    }

    public static void main(String[] args) {
        // 串口连接服务类
        ComConfig comConfig = new ComConfig();
        comConfig.setSerialPortName("COM1");
        comConfig.setBaudRate(9600);
        RxtxServer rxtxServer = new RxtxServer(comConfig);
        rxtxServer.start();
        try {
            // 连接串口需要一点时间,这里稍微等待一下
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 发送数据
        rxtxServer.sendData();
    }
}

测试效果

这里注意,因为解码是按照十六进制解码,所以需要以"十六进制发送","十六进制显示"。

相关推荐
程序猿麦小七1 分钟前
今天给在家介绍一篇基于jsp的旅游网站设计与实现
java·源码·旅游·景区·酒店
张某布响丸辣14 分钟前
SQL中的时间类型:深入解析与应用
java·数据库·sql·mysql·oracle
喜欢打篮球的普通人19 分钟前
rust模式和匹配
java·算法·rust
java小吕布33 分钟前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
慢生活的人。39 分钟前
SpringSecurity+jwt+captcha登录认证授权总结
java·认证·rbac·权限·验证
向阳12181 小时前
LeetCode40:组合总和II
java·算法·leetcode
云空1 小时前
《InsCode AI IDE:编程新时代的引领者》
java·javascript·c++·ide·人工智能·python·php
慧都小妮子1 小时前
Spire.PDF for .NET【页面设置】演示:复制 PDF 文档中的页面
java·pdf·.net
Dr_eamboat1 小时前
【Java】枚举类映射
java·开发语言·python
李少兄1 小时前
解决 Spring Boot 中 `Ambiguous mapping. Cannot map ‘xxxController‘ method` 错误
java·spring boot·后端