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 {