只是记录下使用记录,fd和uid的绑定没有写

引入依赖

<dependency>  
    <groupId>io.netty</groupId>  
    <artifactId>netty-all</artifactId>  
    <version>4.1.22.Final</version>  
</dependency>

创建编码器,解码器

编码器

// 编码器
package com.example.springbootnettyserver.netty;  

import com.example.springbootnettyserver.pojo.MyProtocolBean;  
import io.netty.buffer.ByteBuf;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.handler.codec.MessageToByteEncoder;  

public class MyProtocolEncoder extends MessageToByteEncoder<MyProtocolBean> {  

    @Override  
    protected void encode(ChannelHandlerContext ctx, MyProtocolBean msg, ByteBuf out) throws Exception {  
        if(msg == null){  
            throw new Exception("msg is null");  
        }  
        out.writeByte(0xFA);  
        out.writeByte(0xF5);  

        // 写入长度域(不包含起始域和校验和域)  
        int dataLength = msg.getData().length + 3; // 数据域长度 + 版本域 + 序列号域 + 命令代码  
        out.writeByte(dataLength);  

        // 写入版本域  
        out.writeByte(msg.getVersion());  

        // 写入序列号域  
        out.writeByte(msg.getSequenceNumber());  

        // 写入命令代码  
        out.writeByte(msg.getCommandCode());  
        out.writeBytes(msg.getData());  

        // 计算校验和并写入校验和域  
        byte checksum = calculateChecksum(msg.getCommandCode(), msg.getData());  
        out.writeByte(checksum);  

    }  

    private byte calculateChecksum(byte commandCode, byte[] data) {  
        // 实现校验和的计算逻辑,这里只是示例,具体实现取决于协议中的定义  
        int checksum = 0;  
        checksum += commandCode & 0xFF; // 注意无符号处理  
        for (byte b : data) {  
            checksum += b & 0xFF; // 注意无符号处理  
        }  
        return (byte) (checksum & 0xFF); // 返回校验和的低8位  
    }  
}

解码器

package com.example.springbootnettyserver.netty;  

import com.example.springbootnettyserver.pojo.MyProtocolBean;  
import io.netty.buffer.ByteBuf;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.handler.codec.ByteToMessageDecoder;  

import java.util.List;  

public class MyProtocolDecoder extends ByteToMessageDecoder {  
    private static final int START_FIELD_LENGTH = 2; // 起始域长度  
    private static final byte[] START_FIELD = { (byte) 0xFA, (byte) 0xF5 }; // 起始域固定值  

    @Override  
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {  
        try {  
            if (in.readableBytes() < START_FIELD_LENGTH) {  
                return; // 等待更多的数据以检查起始域  
            }  

            byte[] receivedStartField = new byte[START_FIELD_LENGTH];  
            in.readBytes(receivedStartField);  
            if (!java.util.Arrays.equals(START_FIELD, receivedStartField)) {  
                throw new IllegalStateException("Invalid start field");  
            }  

            if (in.readableBytes() < 5) { // 至少需要读取长度域(1 Byte) + 版本域(1 Byte) + 序列号域(1 Byte) + 命令代码(1 Byte)  
                return;  
            }  
            //if (receivedStartField[3] == (byte) 0x18) {  
            //}  
            int length = in.readByte() & 0xFF; // 读取长度域(1 Byte),注意无符号处理  
            if (!(receivedStartField[0] == (byte) 0xFA && receivedStartField[1] == (byte) 0xF5)) {// 至少包含命令代码(1 Byte)和校验和(1 Byte)  
                throw new IllegalStateException("Invalid length field");  
            }  

            byte version = in.readByte(); // 读取版本域  
            byte sequence = in.readByte(); // 读取序列号域  
            byte commandCode = in.readByte(); // 读取命令代码  
            //if (commandCode == (byte) 0x58) {  
            //    // 在这里处理commandCode等于0x18的情况,例如打印日志  
            //    System.out.println("commandCode is 0x58");  
            //    // 可以在这里添加其他处理逻辑...  
            //}  
            if (in.readableBytes() < length - 3) { // 减去已经读取的字段,检查剩余数据长度  
                return;  
            }  

            byte[] data = new byte[length - 3]; // 数据域长度 = 总长度 - 3(长度域、版本域、序列号域)  
            in.readBytes(data); // 读取数据域  

            byte checksum = calculateChecksum(commandCode, data); // 计算校验和  
            byte receivedChecksum = in.readByte(); // 读取校验和域  

            if (checksum != receivedChecksum) {  
                throw new IllegalStateException("Invalid checksum");  
            }  
            // 创建自定义的消息对象来封装解码后的数据,并添加到输出列表中  
            // 这里假设你有一个 ChargingStationMessage 类来封装这些信息  
            MyProtocolBean message = new MyProtocolBean(version, sequence, commandCode, data);  
            out.add(message);  
        } catch (Exception e) {  
            // 处理异常  
            System.out.println(e.getMessage());  
            // 您可以记录异常、关闭连接等  
            ctx.close(); // 关闭通道作为错误处理的例子  
        }  

    }  
    //这里commandCode我需要根据不同的命令代码处理不同的业务,应该怎么做好一点 ,万一有几十上百个if那肯定不行,请问有什么好的办法,如果有好的方法  
    //        给我个demo  

    private byte calculateChecksum(byte commandCode, byte[] data) {  
        // 实现校验和的计算逻辑,这里只是示例,具体实现取决于协议中的定义  
        int checksum = 0;  
        checksum += commandCode & 0xFF; // 注意无符号处理  
        for (byte b : data) {  
            checksum += b & 0xFF; // 注意无符号处理  
        }  
        return (byte) (checksum & 0xFF); // 返回校验和的低8位  
    }  

}

心跳处理

package com.example.springbootnettyserver.netty;  

import com.example.springbootnettyserver.pojo.MyProtocolBean;  
import io.netty.channel.ChannelHandlerContext;  
import io.netty.handler.timeout.IdleStateEvent;  
import io.netty.handler.timeout.IdleStateHandler;  
import lombok.extern.slf4j.Slf4j;  

import java.nio.charset.StandardCharsets;  
import java.util.concurrent.TimeUnit;  

@Slf4j  
public class ServerIdleStateHandler extends IdleStateHandler {  
    /**  
     * 设置空闲检测时间为 30s  
     */    private static final int READER_IDLE_TIME = 10;  
    public ServerIdleStateHandler() {  
        super(READER_IDLE_TIME, 0, 0, TimeUnit.SECONDS);  
    }  

    @Override  
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {  

        log.info("{} 秒内没有读取到数据,发送心跳包", READER_IDLE_TIME);  
        byte commandCode = 0xA;  // 命令代码为 0xA        String message = "come on baby!!!我是定时任务数据 [version:0xF5][seq:0xF8]";  
        //byte[] data = message.getBytes();  // 将消息转换为字节数组作为数据域  
        //System.out.println(data);  
        byte[] data = message.getBytes(StandardCharsets.UTF_8);  
        log.info("{} 数据:", data);  
        ctx.writeAndFlush(new MyProtocolBean((byte) 0x18, (byte) 0x18, commandCode, data));  
    }
}

package com.example.springbootnettyserver.pojo;  

import lombok.Data;  

@Data  
public class MyProtocolBean {  
    private static final byte START_BYTE_1 = (byte) 0xFA;  
    private static final byte START_BYTE_2 = (byte) 0xF5;  

    private byte version; //版本域  
    private byte sequenceNumber;  //序列号  
    private byte commandCode;   //命令码  
    private byte[] data; //数据域  
    private byte checksum; //校验和  
    private byte length; //校验和  

    // 构造方法  
    public MyProtocolBean(byte version, byte sequenceNumber, byte commandCode, byte[] data) {  
        this.version = version;  
        this.sequenceNumber = sequenceNumber;  
        this.commandCode = commandCode;  
        this.data = data;  
        this.checksum = calculateChecksum();  
        this.length =calculateLength();  
    }  

    private byte calculateLength() {  
        // 长度域 = 长度域本身的长度(1 Byte) + 版本域的长度(1 Byte) + 序列号域的长度(1 Byte)  
        //            + 命令代码的长度(1 Byte) + 数据域的长度 + 校验和域的长度(1 Byte)  
        return (byte) (5 + data.length);  
    }  

    // 计算校验和  
    private byte calculateChecksum() {  
        int sum = version + sequenceNumber + commandCode;  
        for (byte b : data) {  
            sum += b;  
        }  
        return (byte) sum;  
    }  

    // 生成协议报文的字节数组  
    public byte[] toByteArray() {  
        byte[] byteArray = new byte[4 + data.length]; // 4 是起始域、版本域、序列号域和命令代码的长度  

        byteArray[0] = START_BYTE_1;  
        byteArray[1] = START_BYTE_2;  
        byteArray[2] = length;  
        byteArray[3] = version;  
        byteArray[4] = sequenceNumber;  
        byteArray[5] = commandCode;  
        System.arraycopy(data, 0, byteArray, 6, data.length); // 将数据域拷贝到字节数组中  
        byteArray[length - 1] = checksum;  

        return byteArray;  
    }  

}

代码太多,放在github上面了.有需要再看吧
springboot-netty-chargingPile-server