假设客户端向服务端连续发送了两个数据包,分别用ABC和DEF来表示,那么服务端收到的数据可以分为以下三种情况:
第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。
第二种情况,接收端只收到一个数据包,这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
第三种情况,接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。
发生TCP粘包的原因:
发生TCP拆包的原因:
一个TCP报文最大能传输65536个字节,也就是16Kb。
根本原因:TCP是流式协议,数据无边界。
解决问题的根本手段就是找出消息的边界。
netty提供了以下三种方式解决TCP粘包和拆包问题:
DelimiterBasedFrameDecoder是通过发送方每条报文结束都添加特殊符号(例如_)对报文进行切割。
发送方需要自行编码,添加分隔符,编码如下:
ctx.writeAndFlush(Unpooled.copiedBuffer(("hello" + i + "$_").getBytes())); // 以$_结尾
接收方的解码如下:
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1 << 10, Unpooled.copiedBuffer("$_".getBytes())));
缺点:发送的内容本身可能会出现分隔符,需要对发送的内容进行扫描并转义,接收到的内容也要进行反转义。
DelimiterBasedFrameDecoder提供了多个构造方法,最终调用的都是以下构造方法:
public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters)
参数说明:
LineBasedFrameDecoder可以当成是一种特殊的DelimiterBasedFrameDecoder,其分隔符为\n或者\r\n。
发送方的编码如下:
ctx.writeAndFlush(Unpooled.copiedBuffer(("hello" + i + "\n").getBytes())); // 以\n结尾
接收方的解码如下:
ch.pipeline().addLast(new LineBasedFrameDecoder(1 << 10));
FixedLengthFrameDecoder是通过发送方固定每条报文长度均为n个字节,接收方也通过n个字节长度切分报文。
发送方需要自行补齐长度,编码如下:
ctx.writeAndFlush(Unpooled.copiedBuffer(("hello" + i + " ").getBytes())); // 补齐长度为16
接收方的解码如下:
ch.pipeline().addLast(new FixedLengthFrameDecoder(16));
缺点:如果发送的内容比较小,需要补齐长度,空间浪费,如果要发送的内容突然变大,需要调整发送方和接收方的长度。
LengthFieldPrepender对自定义消息长度进行编码。
LengthFieldBasedFrameDecoder对自定义消息长度进行解码。
LengthFieldBasedFrameDecoder的构造方法如下:
public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
参数说明:
更多精彩内容关注本人公众号:架构师升级之路
本文地址:https://blog.csdn.net/u022812849/article/details/107254239
如对本文有疑问, 点击进行留言回复!!
Linux C编程学习笔记(二)暨混乱弥补知识之pragma与字节对齐
荐 opencv学习笔记22:傅里叶变换,高通滤波,低通滤波
[推荐]Linux入门系列(二)文件查看的多种方式(提升效率)
网友评论