> 技术文档 > TCP半包/粘包:网络通信的头号难题,一文彻底解决

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发送缓冲区 → 网络 → TCP接收缓冲区 → 应用层 

三大元凶

  1. Nagle算法:攒小包发大包(粘包)
  2. MTU限制:大包强制拆分(半包)
  3. 缓冲区动态调整:读写速度不一致

三、粘包与半包现场还原 🔍

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); 

四、解决方案大比拼 🛠️

主流解决方案对比表

方案 原理 优点 缺点 固定长度 每条数据固定长度 简单粗暴 浪费带宽 分隔符 特殊符号分割数据 灵活高效 内容需转义 长度字段 头部声明数据长度 精准可靠 实现复杂 自定义协议 应用层协议设计 高度定制 开发成本高

TCP半包/粘包:网络通信的头号难题,一文彻底解决

五、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()); // 业务处理器 

七、不同场景方案选型指南 🧭

场景 推荐方案 示例 命令行交互 行分隔符 Telnet/SSH 即时通讯 长度字段 微信/QQ 物联网设备 固定长度 传感器数据 文件传输 自定义协议 FTP协议

特殊案例: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万并发的聊天服务,正确处理粘包半包问题

点赞关注不迷路! 🚀