TCP半包/粘包:网络通信的头号难题,一文彻底解决
本文用快递拆箱的生动案例,零基础讲透TCP数据传输的核心痛点,手把手教你如何优雅解决粘包拆包问题!
一、快递困局:为什么收到的包裹对不上?📦
想象你在网购平台下单:
#mermaid-svg-pe6lwAXhGEiJ22Wb {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .error-icon{fill:#552222;}#mermaid-svg-pe6lwAXhGEiJ22Wb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pe6lwAXhGEiJ22Wb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .marker.cross{stroke:#333333;}#mermaid-svg-pe6lwAXhGEiJ22Wb svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .cluster-label text{fill:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .cluster-label span{color:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .label text,#mermaid-svg-pe6lwAXhGEiJ22Wb span{fill:#333;color:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .node rect,#mermaid-svg-pe6lwAXhGEiJ22Wb .node circle,#mermaid-svg-pe6lwAXhGEiJ22Wb .node ellipse,#mermaid-svg-pe6lwAXhGEiJ22Wb .node polygon,#mermaid-svg-pe6lwAXhGEiJ22Wb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .node .label{text-align:center;}#mermaid-svg-pe6lwAXhGEiJ22Wb .node.clickable{cursor:pointer;}#mermaid-svg-pe6lwAXhGEiJ22Wb .arrowheadPath{fill:#333333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-pe6lwAXhGEiJ22Wb .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-pe6lwAXhGEiJ22Wb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pe6lwAXhGEiJ22Wb .cluster text{fill:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb .cluster span{color:#333;}#mermaid-svg-pe6lwAXhGEiJ22Wb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pe6lwAXhGEiJ22Wb :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}包裹1包裹2合并包裹打开箱子商家发货快递分拣中心客户商品混杂
这就是TCP粘包/半包问题:
- 粘包:多个快递被装进1个箱子(多条数据合并发送)
- 半包:1个快递被拆成多个箱子(单条数据分开发送)
真实案例:
某游戏服务器因粘包问题,导致玩家移动指令变成\"自杀指令\",引发大规模投诉!
二、根本原因:TCP的\"流式\"特性 🌊
TCP vs UDP 传输差异
关键问题:
应用层数据 → TCP发送缓冲区 → 网络 → TCP接收缓冲区 → 应用层
三大元凶:
- Nagle算法:攒小包发大包(粘包)
- MTU限制:大包强制拆分(半包)
- 缓冲区动态调整:读写速度不一致
三、粘包与半包现场还原 🔍
1. 粘包场景(多个请求合并)
#mermaid-svg-gur5kipAP0FSZMcE {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-gur5kipAP0FSZMcE .error-icon{fill:#552222;}#mermaid-svg-gur5kipAP0FSZMcE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gur5kipAP0FSZMcE .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-gur5kipAP0FSZMcE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gur5kipAP0FSZMcE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gur5kipAP0FSZMcE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gur5kipAP0FSZMcE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gur5kipAP0FSZMcE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gur5kipAP0FSZMcE .marker.cross{stroke:#333333;}#mermaid-svg-gur5kipAP0FSZMcE svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gur5kipAP0FSZMcE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gur5kipAP0FSZMcE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gur5kipAP0FSZMcE .actor-line{stroke:grey;}#mermaid-svg-gur5kipAP0FSZMcE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gur5kipAP0FSZMcE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gur5kipAP0FSZMcE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gur5kipAP0FSZMcE .sequenceNumber{fill:white;}#mermaid-svg-gur5kipAP0FSZMcE #sequencenumber{fill:#333;}#mermaid-svg-gur5kipAP0FSZMcE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gur5kipAP0FSZMcE .messageText{fill:#333;stroke:#333;}#mermaid-svg-gur5kipAP0FSZMcE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gur5kipAP0FSZMcE .labelText,#mermaid-svg-gur5kipAP0FSZMcE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gur5kipAP0FSZMcE .loopText,#mermaid-svg-gur5kipAP0FSZMcE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gur5kipAP0FSZMcE .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gur5kipAP0FSZMcE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gur5kipAP0FSZMcE .noteText,#mermaid-svg-gur5kipAP0FSZMcE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gur5kipAP0FSZMcE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gur5kipAP0FSZMcE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gur5kipAP0FSZMcE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gur5kipAP0FSZMcE .actorPopupMenu{position:absolute;}#mermaid-svg-gur5kipAP0FSZMcE .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gur5kipAP0FSZMcE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gur5kipAP0FSZMcE .actor-man circle,#mermaid-svg-gur5kipAP0FSZMcE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gur5kipAP0FSZMcE :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端服务端发送\"Hello\"立即发送\"World\"收到\"HelloWorld\"客户端服务端
2. 半包场景(大数据被拆分)
#mermaid-svg-uJ9kh7cpIORf0E1b {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b .error-icon{fill:#552222;}#mermaid-svg-uJ9kh7cpIORf0E1b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uJ9kh7cpIORf0E1b .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-uJ9kh7cpIORf0E1b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uJ9kh7cpIORf0E1b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uJ9kh7cpIORf0E1b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uJ9kh7cpIORf0E1b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uJ9kh7cpIORf0E1b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uJ9kh7cpIORf0E1b .marker.cross{stroke:#333333;}#mermaid-svg-uJ9kh7cpIORf0E1b svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uJ9kh7cpIORf0E1b .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uJ9kh7cpIORf0E1b text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-uJ9kh7cpIORf0E1b .actor-line{stroke:grey;}#mermaid-svg-uJ9kh7cpIORf0E1b .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b .sequenceNumber{fill:white;}#mermaid-svg-uJ9kh7cpIORf0E1b #sequencenumber{fill:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b .messageText{fill:#333;stroke:#333;}#mermaid-svg-uJ9kh7cpIORf0E1b .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uJ9kh7cpIORf0E1b .labelText,#mermaid-svg-uJ9kh7cpIORf0E1b .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-uJ9kh7cpIORf0E1b .loopText,#mermaid-svg-uJ9kh7cpIORf0E1b .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-uJ9kh7cpIORf0E1b .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uJ9kh7cpIORf0E1b .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uJ9kh7cpIORf0E1b .noteText,#mermaid-svg-uJ9kh7cpIORf0E1b .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-uJ9kh7cpIORf0E1b .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uJ9kh7cpIORf0E1b .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uJ9kh7cpIORf0E1b .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uJ9kh7cpIORf0E1b .actorPopupMenu{position:absolute;}#mermaid-svg-uJ9kh7cpIORf0E1b .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uJ9kh7cpIORf0E1b .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uJ9kh7cpIORf0E1b .actor-man circle,#mermaid-svg-uJ9kh7cpIORf0E1b line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uJ9kh7cpIORf0E1b :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}客户端服务端发送\"ThisIsALongMessage\"第一次收到\"ThisIs\"第二次收到\"ALongMessage\"客户端服务端
问题代码:
// 错误的服务端读取方式 ByteBuf buf = Unpooled.buffer(1024); channel.read(buf); // 可能读到不完整数据 String msg = buf.toString(CharsetUtil.UTF_8);
四、解决方案大比拼 🛠️
主流解决方案对比表
五、Netty的终极解决方案 💪
1. 固定长度解码器(FixedLengthFrameDecoder)
// 每条数据固定10字节 ch.pipeline().addLast(new FixedLengthFrameDecoder(10));
适用场景:工业传感器等固定长度数据
2. 行分隔符解码器(LineBasedFrameDecoder)
// 按换行符\\n分割 ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
代码测试:
客户端发送: \"Hello\\n\" \"World\\n\" 服务端接收: [Hello] [World]
3. 长度字段解码器(LengthFieldBasedFrameDecoder)✨
最常用方案原理:
#mermaid-svg-XdWotiJA7F8cMWCw {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .error-icon{fill:#552222;}#mermaid-svg-XdWotiJA7F8cMWCw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XdWotiJA7F8cMWCw .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-XdWotiJA7F8cMWCw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XdWotiJA7F8cMWCw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XdWotiJA7F8cMWCw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XdWotiJA7F8cMWCw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XdWotiJA7F8cMWCw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XdWotiJA7F8cMWCw .marker.cross{stroke:#333333;}#mermaid-svg-XdWotiJA7F8cMWCw svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XdWotiJA7F8cMWCw .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .cluster-label text{fill:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .cluster-label span{color:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .label text,#mermaid-svg-XdWotiJA7F8cMWCw span{fill:#333;color:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .node rect,#mermaid-svg-XdWotiJA7F8cMWCw .node circle,#mermaid-svg-XdWotiJA7F8cMWCw .node ellipse,#mermaid-svg-XdWotiJA7F8cMWCw .node polygon,#mermaid-svg-XdWotiJA7F8cMWCw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XdWotiJA7F8cMWCw .node .label{text-align:center;}#mermaid-svg-XdWotiJA7F8cMWCw .node.clickable{cursor:pointer;}#mermaid-svg-XdWotiJA7F8cMWCw .arrowheadPath{fill:#333333;}#mermaid-svg-XdWotiJA7F8cMWCw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XdWotiJA7F8cMWCw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XdWotiJA7F8cMWCw .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-XdWotiJA7F8cMWCw .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-XdWotiJA7F8cMWCw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XdWotiJA7F8cMWCw .cluster text{fill:#333;}#mermaid-svg-XdWotiJA7F8cMWCw .cluster span{color:#333;}#mermaid-svg-XdWotiJA7F8cMWCw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XdWotiJA7F8cMWCw :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}前4字节数据包长度字段真实数据
Netty配置:
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder( 1024 * 1024, // 最大长度 0, // 长度字段偏移量 4, // 长度字段长度(4字节=最大支持2^32数据) 0, // 长度调节值 4 // 跳过字节数(跳过长度字段) ));
六、实战:手写拆包器 💻
场景:自定义聊天协议
+--------+-----------+ | 长度(4) | 消息内容 | +--------+-----------+
1. 编码器(添加长度头)
public class MessageEncoder extends MessageToByteEncoder<String> { @Override protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) { byte[] bytes = msg.getBytes(); out.writeInt(bytes.length); // 写入长度头 out.writeBytes(bytes); // 写入真实数据 } }
2. 解码器(按长度解析)
public class MessageDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) { if (in.readableBytes() < 4) return; // 长度头未完整 in.markReaderIndex(); // 标记读取位置 int length = in.readInt(); // 读取长度头 if (in.readableBytes() < length) { in.resetReaderIndex(); // 重置等待后续数据 return; } byte[] content = new byte[length]; in.readBytes(content); out.add(new String(content)); // 添加完整消息 } }
3. 在Pipeline中使用
ch.pipeline() .addLast(new MessageEncoder()) // 编码器 .addLast(new MessageDecoder()) // 解码器 .addLast(new ChatHandler()); // 业务处理器
七、不同场景方案选型指南 🧭
特殊案例:Redis协议
*3\\r\\n$3\\r\\nSET\\r\\n$5\\r\\nhello\\r\\n$5\\r\\nworld\\r\\n
*3
表示3个元素$3
表示后续3字节数据
八、终极测试:模拟半包/粘包攻击 🧪
测试工具类
public class PacketTestUtil { /** 模拟粘包:合并两条消息 */ public static ByteBuf packTwoMessages(String msg1, String msg2) { ByteBuf buf = Unpooled.buffer(); buf.writeBytes(msg1.getBytes()); buf.writeBytes(msg2.getBytes()); return buf; } /** 模拟半包:拆分消息 */ public static List<ByteBuf> splitMessage(String msg, int... sizes) { List<ByteBuf> parts = new ArrayList<>(); byte[] data = msg.getBytes(); int pos = 0; for (int size : sizes) { parts.add(Unpooled.copiedBuffer(data, pos, size)); pos += size; } return parts; } }
测试用例
// 粘包测试 ByteBuf stickyPacket = PacketTestUtil.packTwoMessages(\"Hello\", \"World\"); channel.writeInbound(stickyPacket); // 应输出两条消息:Hello 和 World // 半包测试 List<ByteBuf> halfPackets = PacketTestUtil.splitMessage(\"HelloWorld\", 3, 7); halfPackets.forEach(channel::writeInbound); // 应输出一条完整消息:HelloWorld
九、避坑指南:常见错误解决方案 🚫
错误1:依赖readableBytes()
// 错误!无法处理半包 ByteBuf buf = ...; if (buf.readableBytes() > 0) { process(buf); }
错误2:固定缓冲区大小
// 错误!大消息被截断 byte[] bytes = new byte[1024]; buf.readBytes(bytes);
正确姿势:使用LengthFieldBasedFrameDecoder
// 最佳实践 pipeline.addLast(new LengthFieldBasedFrameDecoder(maxLength, 0, 4, 0, 4)); pipeline.addLast(new CustomHandler());
十、总结:核心要点梳理 💎
1. 必记概念
#mermaid-svg-e0e5gXdOCO9TP6DI {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .error-icon{fill:#552222;}#mermaid-svg-e0e5gXdOCO9TP6DI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e0e5gXdOCO9TP6DI .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-e0e5gXdOCO9TP6DI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e0e5gXdOCO9TP6DI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e0e5gXdOCO9TP6DI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e0e5gXdOCO9TP6DI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e0e5gXdOCO9TP6DI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e0e5gXdOCO9TP6DI .marker.cross{stroke:#333333;}#mermaid-svg-e0e5gXdOCO9TP6DI svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e0e5gXdOCO9TP6DI .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .cluster-label text{fill:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .cluster-label span{color:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .label text,#mermaid-svg-e0e5gXdOCO9TP6DI span{fill:#333;color:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .node rect,#mermaid-svg-e0e5gXdOCO9TP6DI .node circle,#mermaid-svg-e0e5gXdOCO9TP6DI .node ellipse,#mermaid-svg-e0e5gXdOCO9TP6DI .node polygon,#mermaid-svg-e0e5gXdOCO9TP6DI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e0e5gXdOCO9TP6DI .node .label{text-align:center;}#mermaid-svg-e0e5gXdOCO9TP6DI .node.clickable{cursor:pointer;}#mermaid-svg-e0e5gXdOCO9TP6DI .arrowheadPath{fill:#333333;}#mermaid-svg-e0e5gXdOCO9TP6DI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e0e5gXdOCO9TP6DI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e0e5gXdOCO9TP6DI .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-e0e5gXdOCO9TP6DI .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-e0e5gXdOCO9TP6DI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e0e5gXdOCO9TP6DI .cluster text{fill:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI .cluster span{color:#333;}#mermaid-svg-e0e5gXdOCO9TP6DI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-e0e5gXdOCO9TP6DI :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}TCP粘包/半包原因流式传输Nagle算法MTU限制解决方案固定长度分隔符长度字段
2. Netty最佳实践
解码器选择优先级: 1. LengthFieldBasedFrameDecoder(通用场景) 2. LineBasedFrameDecoder(文本协议) 3. FixedLengthFrameDecoder(固定数据)
3. 终极忠告
🔥 “永远不要相信TCP是可靠传输!应用层必须自己处理消息边界”
动手挑战:
💻 尝试用Netty实现一个支持10万并发的聊天服务,正确处理粘包半包问题
点赞关注不迷路! 🚀