Java实现串口通信

本文提供了RXTXComm和netty两种实现方案,代码框架为springboot

串口所需工具:
http://note.youdao.com/noteshare?id=525f21541ab73b44cd33ec18b45835bc&sub=E57CDCF16EEA45A7BF7331A781C337AB

1、虚拟串口:

创建虚拟串口.png

2、串口调试助手:

串口调试助手.png

3、RXTXComm.jar 包

动态库

window平台:
拷贝 rxtxSerial.dll —> \jre\bin
拷贝 rxtxParallel.dll —> \jre\bin
linux平台:
拷贝 librxtxSerial.so —> /jre/lib/i386/
拷贝 librxtxParallel.so —> /jre/lib/i386/

maven本地

windows:
    mvn install:install-file -DgroupId=com.hik.rxtxcomm -DartifactId=rxtxcomm-windows -Dversion=2.2 -Dpackaging=jar -Dfile=F:\文档\2本周任务\高速移动式巡逻机\软件\mfz-rxtx-2.2-20081207-win-x64\RXTXcomm.jar

linux:
    mvn install:install-file -DgroupId=com.hik.rxtxcomm -DartifactId=rxtxcomm-linux -Dversion=2.2 -Dpackaging=jar -Dfile=F:\文档\2本周任务\高速移动式巡逻机\软件\mfz-rxtx-2.2-20081207-linux-x86_64\RXTXcomm.jar

    -Dfile=jar包实际位置

maven配置



    com.hik.rxtxcomm
    rxtxcomm-windows
    2.2




    com.hik.rxtxcomm
    rxtxcomm-linux
    2.2

4、java实现

简单实现参考此篇:https://www.jianshu.com/p/7c03ad8a6139
vo:

package com.hik.icv.patrol.vo;

import gnu.io.SerialPort;

import java.io.Serializable;

/**
 * @Description 串口参数对象
 * @Author LuoJiaLei
 * @Date 2020/6/8
 * @Time 13:51  
 */
public final class SerialPortParameterVO implements Serializable {

    private static final long serialVersionUID = 8874815910121927542L;

    /**
     * 串口名称(COM0、COM1、COM2等等)
     */
    private String serialPortName;
    /**
     * 波特率
     * 默认:115200
     */
    private int baudRate;
    /**
     * 数据位 默认8位
     * 可以设置的值:SerialPort.DATABITS_5、SerialPort.DATABITS_6、SerialPort.DATABITS_7、SerialPort.DATABITS_8
     * 默认:SerialPort.DATABITS_8
     */
    private int dataBits;
    /**
     * 停止位
     * 可以设置的值:SerialPort.STOPBITS_1、SerialPort.STOPBITS_2、SerialPort.STOPBITS_1_5
     * 默认:SerialPort.STOPBITS_1
     */
    private int stopBits;
    /**
     * 校验位
     * 可以设置的值:SerialPort.PARITY_NONE、SerialPort.PARITY_ODD、SerialPort.PARITY_EVEN、SerialPort.PARITY_MARK、SerialPort.PARITY_SPACE
     * 默认:SerialPort.PARITY_NONE
     */
    private int parity;

    public SerialPortParameterVO(String serialPortName) {
        this.serialPortName = serialPortName;
        this.baudRate = 115200;
        this.dataBits = SerialPort.DATABITS_8;
        this.stopBits = SerialPort.STOPBITS_1;
        this.parity = SerialPort.PARITY_NONE;
    }

    public SerialPortParameterVO(String serialPortName, int baudRate) {
        this.serialPortName = serialPortName;
        this.baudRate = baudRate;
        this.dataBits = SerialPort.DATABITS_8;
        this.stopBits = SerialPort.STOPBITS_1;
        this.parity = SerialPort.PARITY_NONE;
    }

    public String getSerialPortName() {
        return serialPortName;
    }

    public void setSerialPortName(String serialPortName) {
        this.serialPortName = serialPortName;
    }

    public int getBaudRate() {
        return baudRate;
    }

    public void setBaudRate(int baudRate) {
        this.baudRate = baudRate;
    }

    public int getDataBits() {
        return dataBits;
    }

    public void setDataBits(int dataBits) {
        this.dataBits = dataBits;
    }

    public int getStopBits() {
        return stopBits;
    }

    public void setStopBits(int stopBits) {
        this.stopBits = stopBits;
    }

    public int getParity() {
        return parity;
    }

    public void setParity(int parity) {
        this.parity = parity;
    }
}

utils:

package com.hik.icv.patrol.utils;

import com.hik.icv.patrol.vo.SerialPortParameterVO;
import gnu.io.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;

import static com.hik.icv.patrol.common.Constant.SERIALPORT_TIMEOUT;

/**
 * @Description 串口工具类
 * @Author LuoJiaLei
 * @Date 2020/6/8
 * @Time 13:51
 */
public class SerialPortUtil {

    /**
     * @Description 获得系统可用的端口名称列表(COM0 、 COM1 、 COM2等等)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:22  
     * @return java.util.List 可用端口名称列表
     */
    @SuppressWarnings("unchecked")
    public static List getSerialPortList() {
        List systemPorts = new ArrayList<>();
        //获得系统可用的端口
        Enumeration portList = CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {
            String portName = portList.nextElement().getName();//获得端口的名字
            systemPorts.add(portName);
        }
        return systemPorts;
    }

    /**
     * @Description 打开串口(设置串口名称)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:25 
     * @param serialPortName:  串口名称
     * @throws NoSuchPortException               对应串口不存在
     * @throws PortInUseException                串口在使用中
     * @throws UnsupportedCommOperationException 不支持操作操作
     * @return gnu.io.SerialPort 串口对象
     */
    public static SerialPort openSerialPort(String serialPortName)
            throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
        SerialPortParameterVO parameter = new SerialPortParameterVO(serialPortName);
        return openSerialPort(parameter);
    }

    /**
     * @Description 打开串口(设置串口名称与波特率)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:38
     * @param serialPortName: 串口名称
     * @param baudRate: 波特率
     * @throws NoSuchPortException               对应串口不存在
     * @throws PortInUseException                串口在使用中
     * @throws UnsupportedCommOperationException 不支持操作操作
     * @return gnu.io.SerialPort 串口对象
     */
    public static SerialPort openSerialPort(String serialPortName, int baudRate)
            throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
        SerialPortParameterVO parameter = new SerialPortParameterVO(serialPortName, baudRate);
        return openSerialPort(parameter);
    }

    /**
     * @Description 打开串口(设置串口名称、波特率与超时时间)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:41
     * @param serialPortName: 串口名称
     * @param baudRate: 波特率
     * @param timeout: 串口打开超时时间
     * @throws NoSuchPortException               对应串口不存在
     * @throws PortInUseException                串口在使用中
     * @throws UnsupportedCommOperationException 不支持操作操作
     * @return gnu.io.SerialPort 串口对象
     */
    public static SerialPort openSerialPort(String serialPortName, int baudRate, int timeout)
            throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
        SerialPortParameterVO parameter = new SerialPortParameterVO(serialPortName, baudRate);
        return openSerialPort(parameter, timeout);
    }

    /**
     * @Description 打开串口(设置2s超时)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:43
     * @param parameter: 串口参数对象
     * @return gnu.io.SerialPort 串口对象
     */
    public static SerialPort openSerialPort(SerialPortParameterVO parameter)
            throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
        return openSerialPort(parameter, SERIALPORT_TIMEOUT);
    }

    /**
     * @Description 打开串口(通过设置好的参数)
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:49
     * @param parameter: 串口参数对象
     * @param timeout: 串口打开超时时间
     * @throws NoSuchPortException               对应串口不存在
     * @throws PortInUseException                串口在使用中
     * @throws UnsupportedCommOperationException 不支持操作操作
     * @return gnu.io.SerialPort 串口对象
     */
    public static SerialPort openSerialPort(SerialPortParameterVO parameter, int timeout)
            throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
        //通过端口名称得到端口
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(parameter.getSerialPortName());
        //打开端口,(自定义名字,打开超时时间)
        CommPort commPort = portIdentifier.open(parameter.getSerialPortName(), timeout);
        //判断是不是串口
        if (commPort instanceof SerialPort) {
            SerialPort serialPort = (SerialPort) commPort;
            //设置串口参数(波特率,数据位8,停止位1,校验位无)
            serialPort.setSerialPortParams(parameter.getBaudRate(), parameter.getDataBits(), parameter.getStopBits(), parameter.getParity());
            System.out.println("开启串口成功,串口名称:" + parameter.getSerialPortName());
            return serialPort;
        } else {
            //是其他类型的端口
            throw new NoSuchPortException();
        }
    }

    /**
     * @Description 关闭串口
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:54
     * @param serialPort: 要关闭的串口对象
     */
    public static void closeSerialPort(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.close();
            System.out.println("关闭了串口:" + serialPort.getName());
        }
    }

    /**
     * @Description 向串口发送数据
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:54
     * @param serialPort: 串口对象
     * @param data: 发送的数据
     */
    public static void sendData(SerialPort serialPort, byte[] data) {
        OutputStream os = null;
        try {
            //获得串口的输出流
            os = serialPort.getOutputStream();
            os.write(data);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Description 从串口读取数据
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:55
     * @param serialPort: 要读取的串口对象
     * @return byte[] 读取出的数据
     */
    public static byte[] readData(SerialPort serialPort) {
        InputStream is = null;
        byte[] bytes = null;
        try {
            //获得串口的输入流
            is = serialPort.getInputStream();
            //获得数据长度
            int bufflenth = is.available();
            while (bufflenth != 0) {
                //初始化byte数组
                bytes = new byte[bufflenth];
                is.read(bytes);
                bufflenth = is.available();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    /**
     * @Description 给串口设置监听
     * @Author LuoJiaLei
     * @Date 2020/6/10
     * @Time 9:57
     * @param serialPort: 要读取的串口
     * @param listener: SerialPortEventListener监听对象
     * @throws TooManyListenersException 监听对象太多
     */
    public static void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener) throws TooManyListenersException {
        //给串口添加事件监听
        serialPort.addEventListener(listener);
        //设置串口有数据的事件true有效,false无效
        serialPort.notifyOnDataAvailable(true);
        //设置中断事件true有效,false无效
        serialPort.notifyOnBreakInterrupt(true);
    }


}

test:

package com.hik.icv.patrol;

import com.hik.icv.patrol.utils.SerialPortUtil;
import gnu.io.*;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.TooManyListenersException;

public class SerialPortUtilTest {

    /**
     * 测试获取串口列表
     */
    @Test
    public void getSystemPortList() {

        List portList = SerialPortUtil.getSerialPortList();
        System.out.println(portList);

    }

    /**
     * 测试串口打开,读,写操作
     */
    @Test
    public void serialPortAction() {
        try {
            final SerialPort serialPort = SerialPortUtil.openSerialPort("COM2", 115200);
            //启动一个线程每2s向串口发送数据,发送1000次hello
            new Thread(() -> {
                int i = 1;
                while (i < 1000) {
                    String s = "hello";
                    byte[] bytes = s.getBytes();
                    SerialPortUtil.sendData(serialPort, bytes);//发送数据
                    i++;
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //设置串口的listener
            SerialPortUtil.setListenerToSerialPort(serialPort, event -> {
                //数据通知
                if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                    byte[] bytes = SerialPortUtil.readData(serialPort);
                    System.out.println("收到的数据长度:" + bytes.length);
                    System.out.println("收到的数据:" + new String(bytes));
                }
            });
            try {
                // sleep 一段时间保证线程可以执行完
                Thread.sleep(3 * 30 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (NoSuchPortException | PortInUseException | UnsupportedCommOperationException | TooManyListenersException e) {
            e.printStackTrace();
        }
    }

}

5、参考说明

java串口通信API说明

1.1Communications API 简介 
Communications API 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。其中,SerialPort类是用于串口通信的类,ParallePort类是用于并行口通信的类。CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,专用于与端口上的设备进行通信。 
然而,这些类的构造方法都被有意的设置为非公有的(non-public)。所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表;再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,这样,就能得到一个CommPort对象。当然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备。该子类可以是SerialPort类和ParallePort类中的一个。下面将分别对CommPort类,CommPortIdentifier类,串口类SerialPort进行详细的介绍。 

1.2 CommPortIdentifier类的 方法说明
addPortName(String, int, CommDriver) 添加端口名到端口列表里 
addPortOwnershipListener(CommPortOwnershipListener) 添加端口拥有的监听器 
removePortOwnershipListener(CommPortOwnershipListener) 移除端口拥有的监听器 
getCurrentOwner() 得到当前占有端口的对象或应用程序 
getName() 得到端口名称 
getPortIdentifier(CommPort) 得到参数打开的端口的CommPortIdentifier类型对象 
getPortIdentifier(String) 得到以参数命名的端口的CommPortIdentifier类型对象 
getPortIdentifiers() 得到系统中的端口列表 
getPortType() 得到端口的类型 
isCurrentlyOwned() 判断当前端口是否被占用 
open(FileDescriptor) 用文件描述的类型打开端口 
open(String, int) 打开端口,两个参数:程序名称,延迟时间(毫秒数) 

1.3 SerialPort类 
SerialPort关于串口参数的静态成员变量 
成员变量 说明 成员变量 说明 成员变量 说明 
DATABITS_5 数据位为5 STOPBITS_2 停止位为2 PARITY_ODD 奇检验 
DATABITS_6 数据位为6 STOPBITS_1 停止位为1 PARITY_MARK 标记检验 
DATABITS_7 数据位为7 STOPBITS_1_5 停止为1.5 PARITY_NONE 空格检验 
DATABITS_8 数据位为8 PARITY_EVEN 偶检验 PARITY_SPACE 无检验 

SerialPort对象的关于串口参数的函数  
getBaudRate() 得到波特率 getParity() 得到检验类型 
getDataBits() 得到数据位数 getStopBits() 得到停止位数 
setSerialPortParams(int, int, int, int) 设置串口参数依次为(波特率,数据位,停止位,奇偶检验) 

SerialPort关于事件的静态成员变量 
BI Break interrupt中断 FE Framing error错误 
CD Carrier detect载波侦听 OE Overrun error错误 
CTS Clear to send清除以传送 PE Parity error奇偶检验错误 
DSR Data set ready数据备妥 RI Ring indicator响铃侦测 
DATA_AVAILABLE 串口中的可用数据 OUTPUT_BUFFER_EMPTY 输出缓冲区空 

SerialPort中关于事件的方法 
isCD() 是否有载波 isCTS() 是否清除以传送 isDSR() 数据是否备妥 
isDTR() 是否数据端备妥 isRI() 是否响铃侦测 isRTS()   是否要求传送 
addEventListener(SerialPortEventListener)    向SerialPort对象中添加串口事件监听器 
removeEventListener() 移除SerialPort对象中的串口事件监听器 
notifyOnBreakInterrupt(boolean) 设置中断事件true有效,false无效 
notifyOnCarrierDetect(boolean) 设置载波监听事件true有效,false无效 
notifyOnCTS(boolean) 设置清除发送事件true有效,false无效 
notifyOnDataAvailable(boolean) 设置串口有数据的事件true有效,false无效 
notifyOnDSR(boolean) 设置数据备妥事件true有效,false无效 
notifyOnFramingError(boolean) 设置发生错误事件true有效,false无效 
notifyOnOutputEmpty(boolean) 设置发送缓冲区为空事件true有效,false无效 
notifyOnParityError(boolean) 设置发生奇偶检验错误事件true有效,false无效 
notifyOnRingIndicator(boolean) 设置响铃侦测事件true有效,false无效 
getEventType() 得到发生的事件类型返回值为int型 
sendBreak(int) 设置中断过程的时间,参数为毫秒值 
setRTS(boolean) 设置或清除RTS位 
setDTR(boolean) 设置或清除DTR位 

SerialPort中的其他常用方法 
close() 关闭串口 
getOutputStream() 得到OutputStream类型的输出流 
getInputStream() 得到InputStream类型的输入流

6、netty框架实现

maven

4.1.43.Final



    io.netty
    netty-all
    ${netty.version}

NettyClient

package com.hik.icv.patrol.netty;

import com.hik.icv.patrol.utils.ByteUtil;
import com.hik.icv.patrol.utils.HexStringUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.rxtx.RxtxChannel;
import io.netty.channel.rxtx.RxtxChannelConfig;
import io.netty.channel.rxtx.RxtxDeviceAddress;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import static com.hik.icv.patrol.common.Constant.SERIALPORT_BAUDRATE;
import static com.hik.icv.patrol.common.Constant.SERIALPORT_NAME;

/**
 * @Description netty 客户端
 * @Author LuoJiaLei
 * @Date 2020/6/11
 * @Time 16:15
 */
@Component
public class NettyClient {

    @Autowired
    ClientHandler clientHandler;

    //workGroup线程组用于处理任务,可设置cpu数*2,这里设置为4
    //private EventLoopGroup workGroup = new NioEventLoopGroup(4);
    private OioEventLoopGroup workGroup = new OioEventLoopGroup(4);
    //创建netty的启动类
    private Bootstrap bootstrap = new Bootstrap();
    //创建一个Rxtx通道
    private volatile RxtxChannel channel;


    /**
     * @Description 通过netty通信方式连接串口
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 16:10
     */
    @PostConstruct
    public void serialInitial() {
        try {
            //设置线程组
            bootstrap.group(workGroup)
                    //设置通道为阻塞IO
                    //.channel(NioServerSocketChannel.class)
                    //立即发送数据
                    .option(ChannelOption.TCP_NODELAY, true)
                    //TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    //Netty参数,连接超时毫秒数
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                    .channelFactory((ChannelFactory) () -> channel)
                    .handler(new ChannelInitializer() {
                        //设置处理请求的逻辑处理类
                        @Override
                        protected void initChannel(RxtxChannel rc) throws Exception {
                            //ChannelPipeline是handler的任务组,里面有多个handler
                            ChannelPipeline pipeline = rc.pipeline();
                            //watch dog 每30秒钟会发送一条空数据用于检测
                            pipeline.addLast(new IdleStateHandler(30, 0, 0));
                            //配置自定义编码器
                            //pipeline.addLast(new StringEncoder());
                            //pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new SerialMsgEncoder());
                            pipeline.addLast(clientHandler);
                        }
                    });

            channel = new RxtxChannel();
            channel.config().setBaudrate(SERIALPORT_BAUDRATE);
            channel.config().setDatabits(RxtxChannelConfig.Databits.DATABITS_8);
            channel.config().setParitybit(RxtxChannelConfig.Paritybit.EVEN);
            channel.config().setStopbits(RxtxChannelConfig.Stopbits.STOPBITS_1);
            bootstrap.connect(new RxtxDeviceAddress(SERIALPORT_NAME)).sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description netty优雅停机(jvm关闭的时候回收资源)
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 16:29
     */
    @PreDestroy
    protected void serialDestory() {
        workGroup.shutdownGracefully();
    }


    /**
     * @Description 手动写入数据
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 17:32
     * @param message: 字符串
     */
    public void writeAndFlush(String message) {
        //判断信息是否16进制
        if (!HexStringUtil.isHex(message)) {
            //string转16进制
            message = HexStringUtil.stringToHex(message);
        }
        //去空格改大写
        message = message.replace(" ", "").toUpperCase();
        //16进制转byte
        byte[] bytes = ByteUtil.hexStringToBytes(message);
        //比特写入缓存
        ByteBuf buffer = channel.alloc().buffer();
        buffer.writeBytes(bytes);
        channel.writeAndFlush(buffer);
        /*channel.writeAndFlush(Unpooled.copiedBuffer(message, CharsetUtil.UTF_8));*/
    }

}

ClientHandler

package com.hik.icv.patrol.netty;

import com.hik.icv.patrol.utils.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @Description 客户端逻辑处理类
 * @Author LuoJiaLei
 * @Date 2020/6/11
 * @Time 16:36
 */
@Component
@ChannelHandler.Sharable
public class ClientHandler  extends SimpleChannelInboundHandler {

    private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class);

    /**
     * @Description 发送给服务器消息的方法
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 16:34
     * @param ctx:
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,I am client", CharsetUtil.UTF_8));
    }

    /**
     * @Description 回调方法,接收服务器发送的消息
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 16:35
     * @param ctx: ctx
     * @param msg: 消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        byte[] bytes = new byte[msg.readableBytes()];
        msg.getBytes(msg.readerIndex(), bytes);
        String message = new String(bytes, 0, msg.readableBytes());
        System.out.println(message);
    }

    /**
     * @Description 在处理过程中引发异常时被调用
     * @Author LuoJiaLei
     * @Date 2020/6/11
     * @Time 16:36
     * @param ctx: ctx
     * @param cause: 错误原因
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        logger.error(cause.toString());
        ctx.close();
    }

    public void writeAndFlush(Channel channel, String hexString) {
        byte[] bytes = ByteUtil.hexStringToBytes(hexString);
        ByteBuf buffer = channel.alloc().buffer();
        ByteBuf byteBuf = buffer.writeBytes(bytes);
        channel.writeAndFlush(byteBuf);
    }

}

SerialMsgEncoder

package com.hik.icv.patrol.netty;

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

/**
 * @Description netty自定义解码器解决byte消息粘包情况
 * @Author LuoJiaLei
 * @Date 2020/6/11
 * @Time 16:25
 */
public class SerialMsgEncoder extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext ctx, byte[] msg, ByteBuf byteBuf) {
        if (msg.length > 0) {
            byteBuf.writeBytes(msg);
        }
    }

}

源码:https://github.com/luojialei95/patrol-control

你可能感兴趣的:(Java实现串口通信)