Java实现串口通信

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

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

1、虚拟串口:

创建虚拟串口.png

2、串口调试助手:

串口调试助手.png

3、RXTXComm.jar 包

动态库

window平台:
拷贝 rxtxSerial.dll —> <JAVA_HOME>\jre\bin
拷贝 rxtxParallel.dll —> <JAVA_HOME>\jre\bin
linux平台:
拷贝 librxtxSerial.so —> <JAVA_HOME>/jre/lib/i386/
拷贝 librxtxParallel.so —> <JAVA_HOME>/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配置

<!-- windows 串口工具 -->
<dependency>
    <groupId>com.hik.rxtxcomm</groupId>
    <artifactId>rxtxcomm-windows</artifactId>
    <version>2.2</version>
</dependency>

<!-- linux 串口工具 -->
<dependency>
    <groupId>com.hik.rxtxcomm</groupId>
    <artifactId>rxtxcomm-linux</artifactId>
    <version>2.2</version>
</dependency>

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<java.lang.String> 可用端口名称列表
     */
    @SuppressWarnings("unchecked")
    public static List<String> getSerialPortList() {
        List<String> systemPorts = new ArrayList<>();
        //获得系统可用的端口
        Enumeration<CommPortIdentifier> 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<String> 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

<netty.version>4.1.43.Final</netty.version>

<!-- netty -->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>${netty.version}</version>
</dependency>

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<RxtxChannel>) () -> channel)
                    .handler(new ChannelInitializer<RxtxChannel>() {
                        //设置处理请求的逻辑处理类
                        @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<ByteBuf> {

    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<byte[]> {

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

}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352