一、大小端原理(java默认采用大端、补码存储)
#java大小端原理:大端(高位字节存储在低地址,低位字节存储在高地址),小端(高位字节储存在高地址,低位字节存储在低地址)
public static void main(String[] args) {
//java大小端原理:大端(高位字节存储在低地址,低位字节存储在高地址),小端(高位字节储存在高地址,低位字节存储在低地址)
ByteBuf buf = Unpooled.buffer(2);
//大端模式写入
buf.writeInt(1254);
//大端模式写入
buf.writeIntLE(1254);
System.out.println( "字节数组:" + Arrays.toString(buf.array()));
//大端字节
byte[] bigByte = {0, 0, 4, -26};
//小端字节
byte[] smallByte = {-26, 4, 0,0};
//10001 1010 = -26
System.out.println( "-26 = " + Integer.toBinaryString(-26));
System.out.println( "-26 二进制补码:" + "11111111111111111111111111100110");
System.out.println( "-26 二进制反码:" + "11111111111111111111111111100101");
System.out.println( "-26 二进制原码:" + "10000000000000000000000000011010");
//00000 0100 = 4
System.out.println( "4 = " + Integer.toBinaryString(4));
System.out.println( "4 二进制补码:" + "00000000000000000000000000000100");
System.out.println( "4 二进制反码:" + "00000000000000000000000000000100");
System.out.println( "4 二进制原码:" + "00000000000000000000000000000100");
//大端解码
int big = (((bigByte[0]) << 24 & 0xFF000000)
| ((bigByte[0+1]<<16) & 0xFF0000)
| ((bigByte[0+2]<<8)& 0xFF00)
| ((bigByte[0+3]) & 0xFF));
System.out.println("大端解码:" + big);
//小端解码
int small = ((smallByte[0] & 0xFF)
| ((smallByte[0+1]<<8) & 0xFF00)
| ((smallByte[0+2]<<16)& 0xFF0000)
| ((smallByte[0+3]<<24) & 0xFF000000));
System.out.println("小端解码:" + small);
System.out.print( "补码方式定义小端-解码:");
System.out.println(
((0b11111111111111111111111111100110) & 0xff |
((4) << 8) & 0xff00 |
((0) << 16) & 0xff0000) |
((0) << 24) & 0xff000000);
//小端解码 -8587
byte[] e = {(byte) 0x75,(byte) 0xde};
System.out.println("crc16-小端解码:" + (short)(((e[1] << 8) & 0xff00) | e[0] & 0xff));
}
/*
***************控制台打印运行结果***************
字节数组:[0, 0, 4, -26, -26, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
-26 = 11111111111111111111111111100110
-26 二进制补码:11111111111111111111111111100110
-26 二进制反码:11111111111111111111111111100101
-26 二进制原码:10000000000000000000000000011010
4 = 100
4 二进制补码:11111111111111111111111111100110
4 二进制反码:11111111111111111111111111100101
4 二进制原码:10000000000000000000000000011010
大端解码:1254
小端解码:1254
补码方式定义小端-解码:1254
或运算原理(小端解码):1254
crc16-小端解码:-8587
*/
二、网络传输中粘包、半包处理
//自定义解码方法
int length, head;
while (true) {
byteBuf.markReaderIndex();
head = byteBuf.readIntLE();
length = byteBuf.readIntLE();
if (length >= 18&& head == 0xAABBCCDD) {
logger.debug("head:{},length:{}", head, length);
break;
}
byteBuf.resetReaderIndex();
byte temp = byteBuf.readByte();
logger.debug("skip a byte:{},channelId:{}", temp, channelHandlerContext.channel().id().asShortText());
if (byteBuf.readableBytes() < BASE_LENGTH) {
return;
}
}
//desc:使用netty解码器解解决网络粘包问题
//param1: 指定小端解码 (ByteOrder.LITTLE_ENDIAN)
//param2: 单个包最大长度 (Integer.MAX_VALUE,)
//param3: 长度字段开始位置偏移量 (4)
//param4: 长度字段所占字节数 (4)
//param5: 长度字段默认表示后续报文的长度 (-8)
//param6: 从解码帧中剥离的第一个字节数(0表示解码过程中不丢弃任何数据)(0)
//param7: 默认为true,表示读取到长度域的值超过Integer.MAX_VALUE时抛异常,false表示真正读取完才抛异常,建议不要修改,否则可能会造成内存溢出 (true)
LengthFieldBasedFrameDecoderlengthFieldBasedFrameDecoder =
new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN,Integer.MAX_VALUE, 4, 4, -8, 0,true);
/**
*@Desc:自定义IEC04协议粘包处理
*/
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
// 记录包头开始的index
int beginReader;
int newDataLength = 0;
while (true) {
// 获取包头开始的index
beginReader = buffer.readerIndex();
// 记录一个标志用于重置
buffer.markReaderIndex();
// 读到了协议的开始标志,结束while循环
if (buffer.readByte() == Iec104Constant.HEAD_DATA) {
// 标记当前包为新包
//读取包长度
byte newDataLengthByte = buffer.readByte();
newDataLength = newDataLengthByte & 0xFF;
break;
}
continue;
}
if (buffer.readableBytes() < newDataLength) {
buffer.readerIndex(beginReader);
return;
}
newDataLength = newDataLength + 2;
//恢复指针
buffer.readerIndex(beginReader);
ByteBuf data = buffer.readBytes(newDataLength);
out.add(data);
}
私有报文协议