基于 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 小时前
ps aux | grep smart_webrtc这条指令代表什么意思
java·linux·ubuntu·webrtc
弥琉撒到我2 小时前
微服务swagger解析部署使用全流程
java·微服务·架构·swagger
一颗花生米。2 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
问道飞鱼2 小时前
Java基础-单例模式的实现
java·开发语言·单例模式
ok!ko6 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
2402_857589366 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
吾爱星辰7 小时前
Kotlin 处理字符串和正则表达式(二十一)
java·开发语言·jvm·正则表达式·kotlin
哎呦没8 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
编程、小哥哥8 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
IT学长编程9 小时前
计算机毕业设计 玩具租赁系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·玩具租赁系统