只是记录下使用记录,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
已有 0 条评论