client 連續發送 server 的數據包,server 接收到數據會出現數據包粘在一塊兒的狀況java
好比 client發送了 數據"123456"和"78910"server收到倒是: "12345" 和 "678910"app
TCP報文格式 以下, ide
在 TCP首部並未指明數據包的長度大數據
- TCP首部中有20bytes的固定長度;
- 可於第1個報文中指明: 最大報文段長度MSS(Maximum Segment Size); 可是它是選項 部分, 非必有的;
通常解決粘包問題的四種方案:ui
客戶端發送數據包時, 固定長度, 好比 1024字節, 若是某次發包不足 1024字節, 空格補足;
客戶端發包時, 每一個包末尾使用固定分隔符, 好比"rn";若是數據包粘包了, 拆包時, 就等下一個數據包直到拿到"rn";this
拆後的頭部部分與前一個包的剩餘部分合併; 這樣就獲得一個完整的包.編碼
消息分爲 頭部 和 消息體; 而後在頭部增設一個 消息長度的字段; 接收時, 讀到夠len長度的數據, 纔算讀完完整數據!
本身定製本身的發包協議; 指定數據長度和分拆合併邏輯;
FixedLengthFrameDecoder:spa
public class FixedLengthFrameDecoder extends ByteToMessageDecoder { private final int frameLength; ... }
將接收到的字節按固定字節數分割。例如,若是你收到如下四個片斷化的數據包:.net
第1個packet | 第2個packet | 第3個packet | 第4個packet |
---|---|---|---|
A | BC | DEFG | HI |
FixedLengthFrameDecoder(3)
將其解碼爲如下3個固定長度的數據包:netty
第1個packet | 第2個packet | 第3個packet |
---|---|---|
ABC | DEF | GHI |
發包前編碼:
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class FixedLengthFrameEncoder extends MessageToByteEncoder<String> { private int len; public FixedLengthFrameEncoder(int len) { this.len = len; } @Override protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception { // 超長直接拋出異常 if (msg.length() > len) { throw new UnsupportedOperationException("message too large, limited " + len); } // 不足長補全 if (msg.length() < len) { msg = appendSpace(msg); } ctx.writeAndFlush(Unpooled.wrappedBuffer(msg.getBytes())); } // 進行空格補全 /** * 補空格 * @param msg * @return */ private String appendSpace(String msg) { StringBuilder builder = new StringBuilder(msg); for (int i = 0; i < len - msg.length(); i++) { builder.append(" "); } return builder.toString(); } }
public class LineBasedFrameDecoder extends ByteToMessageDecoder {}
public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {}
LengthFieldBasedFrameDecoder & LengthFieldPrepender
增長長度字段, 標明數據長度;maxFrameLength:指定包所傳遞的最大數據包大小;
lengthFieldOffset:指定length字段在字節碼中的偏移量;
lengthFieldLength:指定length字段所佔用的字節長度;
lengthAdjustment:對含消息頭和消息體的, 咱們有時需進行消息頭的長度調整,方便只取消息體: 此字段就是消息頭長;
initialBytesToStrip:對於length字段在消息頭中間的狀況,能夠經過此字段, 忽略消息頭及length字段所佔的字節。
收包後解碼:
public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder { private final ByteOrder byteOrder; private final int maxFrameLength; private final int lengthFieldOffset; private final int lengthFieldLength; private final int lengthFieldEndOffset; private final int lengthAdjustment; private final int initialBytesToStrip; private final boolean failFast; private boolean discardingTooLongFrame; private long tooLongFrameLength; private long bytesToDiscard; ... }
發包前編碼:
public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> { private final ByteOrder byteOrder; private final int lengthFieldLength; private final boolean lengthIncludesLengthFieldLength; private final int lengthAdjustment; ... }
繼承這兩個類, 而後複寫本身的邏輯
public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter{} public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {