netty通訊需要對數據進行編碼,解碼,於是我們需要用到netty的編碼器、解碼器
netty 提供的解碼器
DelimiterBasedFrameDecoder 解決TCP的粘包解碼器
StringDecoder 消息轉成String解碼器
LineBasedFrameDecoder 自動完成標識符分隔解碼器
FixedLengthFrameDecoder 固定長度解碼器,二進制
Base64Decoder base64 解碼器
netty 提供的編碼器
Base64Encoder base64編碼器
StringEncoder 消息轉成String編碼器
LineBasedFrameDecoder 自動完成標識符分隔編碼器
MessageToMessageEncoder 根據 消息對象 編碼為消息對象
對於 netty的數據傳遞都是ByteBuf,我們一般重寫以上的解碼器、編碼器來實現自己的邏輯
1、DelimiterBasedFrameDecoder 解決TCP的粘包解碼器
IODecoder 繼承
/** * 解碼 * DelimiterBasedFrameDecoder 防止 沾包 * @author flm * 2017年10月30日 */ public class IODecoder extends DelimiterBasedFrameDecoder { public static final AttributeKey<DeviceSession> KEY = AttributeKey.valueOf("IO"); // 保存 private static final Logger log = Logger.getLogger(IODecoder.class); // 防止 沾包 分隔符 private static ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes()); // 沾包 分割符 \n private static int maxFrameLength = 1024 * 6; //數據大小 public IODecoder() { super(maxFrameLength, delimiter); } /** * 重新 自定義解碼 */ @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { // 對數據 buffer 解碼 return super.decode(ctx, buffer); } }
2、MessageToMessageEncoder 編碼器
/** * 指令 編碼 * MessageToMessageEncoder<PushEntity> * 把 PushEnty 編碼為string * @author flm * 2017年11月3日 */ public class IOEncoder extends MessageToMessageEncoder<PushEntity> { private static final Logger LOG = Logger.getLogger(IOEncoder.class); public IOEncoder() { super(); } /** * 重寫 編碼 */ @Override protected void encode(ChannelHandlerContext ctx, PushEntity msg, List<Object> out) throws Exception { try { PushEntity push = (PushEntity) msg; } // 以字符串 形式 發送 out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg.toString()), Charset.defaultCharset())); } catch (Exception e) { e.printStackTrace(); } } }
3、 FixedLengthFrameDecoder 固定長度解碼器,二進制
/** * * 功能描述:協議消息解碼器
* 把 btyeBuf 轉為 RootMessage對象 * */ public class GT06MsgDecoder extends LengthFieldBasedFrameDecoder { public GT06MsgDecoder() { super(65540, 2, 1, 2, 0); //繼承 }
/*
* 重寫 解碼
*/ @Override public Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { ByteBuf frame = (ByteBuf) super.decode(ctx, in);
// 讀取 ByteBuf 是根據 位數來讀取的 try { if (frame == null) { return null; } int frameLen = frame.readableBytes(); // 起始位 byte[] header = new byte[GT06Constant.START_DELIMITER_LEN]; frame.readBytes(header); // 是否是0x79 0x79 開頭的擴展包 boolean extPacket = false; if(Arrays.equals(GT06Constant.PACKET_START_EXT, header)) { extPacket = true; } int contentLen = MessageUtils.getContentLen(frameLen, extPacket); // 跳過包長度 frame.skipBytes(MessageUtils.getPacketSizeLen(extPacket)); // 消息內容 byte[] msgContent = new byte[contentLen]; // 消息序列號 byte[] sequence = new byte[GT06Constant.MESSAGE_SERIAL_LEN]; // crc校驗碼 byte[] crc = new byte[GT06Constant.CRC_ITU_LEN]; // 終止符 byte[] endDelimiter = new byte[GT06Constant.END_DELIMITER_LEN];
return new RootMessage(action, sequence, msgContent); } finally { if(frame != null) { frame.release(); } } }
其它的編碼器,解碼器都大同小異,不懂的可以看源碼
其實解碼、編碼,最最重要的是對BtyeBuf的讀取
BtyeBuf讀操作主要提供以下功能:
- readByte:取1字節的內容;
- skipBytes: 跳過內容
- readUnsignedByte:取1字節的內容,返回(
(short) (readByte() & 0xFF)
);(能把負數轉換為無符號嗎?) - readShort:取2字節的內容,返回轉換后的
short
類型; - readUnsignedShort:取2字節的內容,返回
readShort() & 0xFFFF
; - readMedium:取3字節的內容,返回轉換后的
int
類型; - readUnsignedMedium:取3字節的內容,返回轉換后的
int
類型; - readInt:取4字節的內容;
- readUnsignedInt:取4字節的內容,返回
readInt() & 0xFFFFFFFFL
; - readLong:取8字節的內容;
- readChar:取1字節的內容;
- readFloat:取4字節的int內容,轉換為
float
類型; - readDouble:取8字節的long內容,轉換為
double
類型; - readBytes:取指定長度的內容,返回
ByteBuf
類型; - readSlice:取指定長度的內容,返回
ByteBuf
類型; - readBytes:取指定長度的內容到目標容器。
寫操作
寫操作提供的功能主要是往ByteBuf中寫入byte內容,不再一一贅述。主要區別在於寫入前根據類型轉換為相對應長度的byte數組。
主要函數是:writeBoolean、writeByte、writeShort、writeMedium、writeInt、writeLong、writeChar、writeFloat、writeDouble、writeBytes、writeZero。
邊界值安全
不論讀或寫,肯定會存在ByteBuf數據為空或滿的情形,作為數據容器,要存在邊界值檢查,確保讀寫安全。