[{"data":1,"prerenderedAt":374},["ShallowReactive",2],{"\u002Fjava-interview\u002Fnetworking":3,"java-interview-search:\u002Fjava-interview\u002Fnetworking":329},{"id":4,"title":5,"body":6,"category":315,"cover":316,"date":317,"description":318,"extension":319,"group":320,"meta":321,"navigation":322,"order":323,"path":324,"seo":325,"sitemap":326,"stem":327,"__hash__":328},"javaInterview\u002Fjava-interview\u002Fnetworking.md","计算机网络：TCP、HTTP 与 Netty",{"type":7,"value":8,"toc":300},"minimark",[9,13,18,21,24,32,38,49,54,62,76,87,91,94,97,100,103,107,114,117,136,139,143,146,149,152,155,159,162,165,168,171,175,190,193,203,206,210,213,216,231,234,237,255,261,264],[10,11,12],"p",{},"网络题看起来零散，其实可以围绕「连接怎么建立、数据怎么可靠送达、协议怎么演进、框架怎么封装」四条线来准备。",[14,15,17],"h2",{"id":16},"_1-tcp-为什么是三次握手四次挥手","1. TCP 为什么是三次握手、四次挥手？",[10,19,20],{},"三次握手：客户端发 SYN，服务端回 SYN-ACK，客户端再回 ACK。两次不够，是因为服务端无法确认客户端收到了自己的 SYN；同时也能防止网络中滞留的旧连接请求突然到达，造成错误连接。第三次 ACK 让双方都确认「收发能力正常、初始序列号已同步」。",[10,22,23],{},"四次挥手是因为 TCP 是全双工的：主动方发 FIN 只表示「我没有数据要发了」，对方还可以继续发送，所以要先回 ACK，等自己的数据发完再发 FIN。主动方收到对方的 FIN 后回 ACK，连接才完全关闭。",[10,25,26,27,31],{},"主动关闭方会进入 ",[28,29,30],"code",{},"TIME_WAIT","，等待 2MSL。目的有两个：保证最后一个 ACK 能到达对方，否则对方会重发 FIN；让本连接中的旧报文在网络中消失，避免影响使用相同四元组的新连接。",[10,33,34,35,37],{},"高并发短连接的服务端容易出现大量 ",[28,36,30],{},"，解决方向是连接复用、长连接，而不是盲目调小等待时间。",[14,39,41,42,44,45,48],{"id":40},"_2-time_wait-和-close_wait-分别意味着什么","2. ",[28,43,30],{}," 和 ",[28,46,47],{},"CLOSE_WAIT"," 分别意味着什么？",[10,50,51,53],{},[28,52,30],{}," 出现在主动关闭方，是正常的收尾状态，通常持续 2MSL。数量很大往往说明短连接太多，或者服务端主动关闭了连接；优先考虑连接池和 keep-alive。",[10,55,56,58,59,61],{},[28,57,47],{}," 出现在被动关闭方，表示「对方已经发了 FIN，但本端应用还没有调用 close」。大量 ",[28,60,47],{}," 几乎总是代码问题：连接没有在 finally 中关闭、异常路径漏了释放、连接池配置不当。",[10,63,64,65,68,69,72,73,75],{},"排查时用 ",[28,66,67],{},"ss -tan state close-wait"," 或 ",[28,70,71],{},"netstat"," 看数量，再结合进程和线程栈定位是哪段代码持有连接。",[28,74,47],{}," 不会自己消失，必须由应用关闭。",[10,77,78,79,82,83,86],{},"还要区分 ",[28,80,81],{},"ESTABLISHED"," 过多（并发高或泄漏）和 ",[28,84,85],{},"SYN_RECV"," 过多（可能的 SYN Flood）。",[14,88,90],{"id":89},"_3-tcp-如何保证可靠传输","3. TCP 如何保证可靠传输？",[10,92,93],{},"每个字节都有序号，接收方用累积确认告诉发送方「我已经收到了哪里」。超时未确认会重传；收到三个重复 ACK 时触发快速重传，不必等到超时。校验和用于发现传输错误。",[10,95,96],{},"滑动窗口做流量控制：接收方通过窗口字段告诉发送方还能接收多少，避免发送过快压垮接收端。窗口扩大选项让高带宽高延迟链路也能有足够吞吐。",[10,98,99],{},"拥塞控制则根据网络状况调整发送速率，包括慢启动、拥塞避免、快速恢复等阶段。现代内核常用 CUBIC，部分场景用 BBR，它们对丢包和延迟的假设不同。",[10,101,102],{},"TCP 保证的是「可靠、有序的字节流」，不保证应用层消息边界，也不保证实时性。UDP 不建立连接、不重传，适合实时音视频、DNS 这类能容忍少量丢包的场景。",[14,104,106],{"id":105},"_4-粘包拆包是怎么产生的怎么解决","4. 粘包拆包是怎么产生的？怎么解决？",[10,108,109,110,113],{},"TCP 是字节流，没有消息边界。发送方两次 ",[28,111,112],{},"write"," 的数据可能被合并成一个包到达，也可能一条消息被拆成多个包，这就是「粘包」和「拆包」。UDP 有数据报边界，不存在这个问题，但会丢包和乱序。",[10,115,116],{},"解决办法是在应用层定义边界：定长消息、分隔符、或者最常用的「长度字段 + 消息体」。长度字段方案最稳妥，能处理任意二进制内容，也是大多数 RPC 协议的选择。",[10,118,119,120,123,124,127,128,131,132,135],{},"Netty 提供了现成的解码器：",[28,121,122],{},"LengthFieldBasedFrameDecoder"," 处理长度前缀，",[28,125,126],{},"DelimiterBasedFrameDecoder"," 处理分隔符，",[28,129,130],{},"FixedLengthFrameDecoder"," 处理定长。还要配合 ",[28,133,134],{},"IdleStateHandler"," 处理空闲连接。",[10,137,138],{},"注意长度字段的字节序、最大长度限制和半包缓存，恶意超长长度可能造成内存耗尽。",[14,140,142],{"id":141},"_5-http11http2http3-有什么区别","5. HTTP\u002F1.1、HTTP\u002F2、HTTP\u002F3 有什么区别？",[10,144,145],{},"HTTP\u002F1.1 支持持久连接，但一个连接同一时刻通常只处理一个请求，浏览器靠开多个连接提升并发；管道化理论上可以连续发请求，但因队头阻塞和代理兼容问题很少使用。",[10,147,148],{},"HTTP\u002F2 引入二进制分帧和多路复用，一个 TCP 连接上可以并行多个流，头部用 HPACK 压缩，减少了连接数和头部开销。但 TCP 层仍有队头阻塞：一个包丢失，所有流都要等重传。",[10,150,151],{},"HTTP\u002F3 基于 QUIC（UDP），流之间独立，丢包只影响所在流；内置 TLS 1.3，支持 0-RTT 恢复，连接迁移也更平滑。代价是 UDP 在部分网络中被限制，运维和排查方式与 TCP 不同。",[10,153,154],{},"选型时还要考虑中间设备和服务端支持，HTTP\u002F2 的收益在高延迟、多资源场景最明显。",[14,156,158],{"id":157},"_6-https-的握手过程是怎样的","6. HTTPS 的握手过程是怎样的？",[10,160,161],{},"客户端先发 ClientHello，包含支持的协议版本、加密套件和随机数；服务端回 ServerHello 和证书链。客户端验证证书：是否由受信任 CA 签发、域名是否匹配、是否在有效期内、是否被吊销。",[10,163,164],{},"验证通过后，双方通过密钥交换算法协商出共享密钥。现代 TLS 使用 ECDHE，具备前向安全：即使服务器私钥泄露，也无法解密过去的会话。之后用对称加密传输数据，因为对称加密性能更好。",[10,166,167],{},"TLS 1.3 把握手压缩到 1-RTT，并支持会话恢复时的 0-RTT，但 0-RTT 数据存在重放风险，只适合幂等请求。",[10,169,170],{},"中间人攻击的前提是客户端信任了攻击者的证书，所以证书校验、HSTS 和证书透明度都很重要。HTTPS 保护传输，不保护服务端被入侵后的数据。",[14,172,174],{"id":173},"_7-cookiesessiontokenjwt-怎么选","7. Cookie、Session、Token、JWT 怎么选？",[10,176,177,178,181,182,185,186,189],{},"Cookie 是浏览器存储机制，",[28,179,180],{},"HttpOnly"," 防止脚本读取，",[28,183,184],{},"Secure"," 只走 HTTPS，",[28,187,188],{},"SameSite"," 缓解 CSRF。Session 把状态放在服务端，浏览器只存 session id，撤销方便，但需要共享存储。",[10,191,192],{},"Token 是广义的凭证，JWT 是其中一种自包含格式：头部、载荷、签名。服务端验签即可，不必查存储，适合水平扩展；但载荷是明文可读的，不能放敏感信息，撤销也需要额外机制。",[10,194,195,196,199,200,202],{},"常见组合是「短期 access token + 长期 refresh token」，敏感操作再校验权限或黑名单。把 JWT 放在 ",[28,197,198],{},"localStorage"," 容易受 XSS 影响，放在 ",[28,201,180],{}," Cookie 又要防 CSRF，需要配套措施。",[10,204,205],{},"选择的核心是状态放在哪、撤销要求多强、是否有跨域和跨端需求，而不是「JWT 更现代」。",[14,207,209],{"id":208},"_8-netty-为什么高性能零拷贝是什么","8. Netty 为什么高性能？零拷贝是什么？",[10,211,212],{},"Netty 用主从 Reactor 模型：boss 线程组负责接收连接，worker 线程组负责读写事件；每个 Channel 绑定一个 EventLoop，事件在单线程内串行处理，避免锁竞争。",[10,214,215],{},"它基于 NIO 多路复用，支持海量连接；ByteBuf 用读指针和写指针分离读写位置，支持池化和堆外内存，减少分配与复制；Pipeline 用责任链组织编解码和业务处理，扩展方便。",[10,217,218,219,222,223,226,227,230],{},"「零拷贝」在不同语境下含义不同：操作系统层面是 ",[28,220,221],{},"sendfile"," 让数据不经过用户态；Netty 层面更多是 ",[28,224,225],{},"CompositeByteBuf"," 组合多个缓冲区、",[28,228,229],{},"FileRegion"," 传输文件、以及用堆外内存减少 JVM 堆与内核之间的复制。它不等于「完全不拷贝」。",[10,232,233],{},"使用时要防止在 EventLoop 里执行阻塞操作，否则会卡住该线程上的所有连接；还要注意 ByteBuf 的引用计数和泄漏检测，堆外内存泄漏不会体现在堆 dump 里。",[14,235,236],{"id":236},"易错点与自测",[238,239,240,244,252],"ul",{},[241,242,243],"li",{},"三次握手不是为了「确认双方在线」这么简单，核心是序列号同步和防止旧连接。",[241,245,246,248,249,251],{},[28,247,30],{}," 是主动关闭方的正常状态，",[28,250,47],{}," 堆积多半是应用没关连接。",[241,253,254],{},"HTTP\u002F2 解决了 HTTP 层队头阻塞，没有解决 TCP 层；HTTP\u002F3 才换掉 TCP。",[10,256,257,258,260],{},"自测：① 解释 2MSL 的两个作用；② 说明长度字段解码器如何处理半包；③ 区分 ",[28,259,221],{}," 零拷贝与 Netty 的缓冲区组合。",[14,262,263],{"id":263},"参考资料",[238,265,266,276,284,292],{},[241,267,268,275],{},[269,270,274],"a",{"href":271,"rel":272},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9293.html",[273],"nofollow","RFC 9293：TCP"," — 连接管理、重传与流量控制的规范",[241,277,278,283],{},[269,279,282],{"href":280,"rel":281},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9110.html",[273],"RFC 9110：HTTP 语义"," — 方法、状态码与连接语义",[241,285,286,291],{},[269,287,290],{"href":288,"rel":289},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9000.html",[273],"RFC 9000：QUIC"," — HTTP\u002F3 的传输基础",[241,293,294,299],{},[269,295,298],{"href":296,"rel":297},"https:\u002F\u002Fnetty.io\u002Fwiki\u002Fuser-guide-for-4.x.html",[273],"Netty 4.x 用户指南"," — Reactor 模型、Pipeline 与编解码器",{"title":301,"searchDepth":302,"depth":302,"links":303},"",2,[304,305,307,308,309,310,311,312,313,314],{"id":16,"depth":302,"text":17},{"id":40,"depth":302,"text":306},"2. TIME_WAIT 和 CLOSE_WAIT 分别意味着什么？",{"id":89,"depth":302,"text":90},{"id":105,"depth":302,"text":106},{"id":141,"depth":302,"text":142},{"id":157,"depth":302,"text":158},{"id":173,"depth":302,"text":174},{"id":208,"depth":302,"text":209},{"id":236,"depth":302,"text":236},{"id":263,"depth":302,"text":263},"java-interview",null,"2026-10-24","从握手挥手、可靠传输到 HTTP\u002F2、HTTPS 和 Netty 的线程模型，梳理网络面试的高频考点与边界","md","分布式与工程",{},true,"130","\u002Fjava-interview\u002Fnetworking",{"title":5,"description":318},{"loc":324},"java-interview\u002Fnetworking","T3HAA_xnaKydTaGLblK09SA1O-slDi3gBNQgMLZzce8",[330,334,338,342,346,350,354,358,362,366,370],{"id":324,"title":5,"titles":331,"content":332,"level":333},[],"从握手挥手、可靠传输到 HTTP\u002F2、HTTPS 和 Netty 的线程模型，梳理网络面试的高频考点与边界 网络题看起来零散，其实可以围绕「连接怎么建立、数据怎么可靠送达、协议怎么演进、框架怎么封装」四条线来准备。",1,{"id":335,"title":17,"titles":336,"content":337,"level":302},"\u002Fjava-interview\u002Fnetworking#_1-tcp-为什么是三次握手四次挥手",[5],"三次握手：客户端发 SYN，服务端回 SYN-ACK，客户端再回 ACK。两次不够，是因为服务端无法确认客户端收到了自己的 SYN；同时也能防止网络中滞留的旧连接请求突然到达，造成错误连接。第三次 ACK 让双方都确认「收发能力正常、初始序列号已同步」。 四次挥手是因为 TCP 是全双工的：主动方发 FIN 只表示「我没有数据要发了」，对方还可以继续发送，所以要先回 ACK，等自己的数据发完再发 FIN。主动方收到对方的 FIN 后回 ACK，连接才完全关闭。 主动关闭方会进入 TIME_WAIT，等待 2MSL。目的有两个：保证最后一个 ACK 能到达对方，否则对方会重发 FIN；让本连接中的旧报文在网络中消失，避免影响使用相同四元组的新连接。 高并发短连接的服务端容易出现大量 TIME_WAIT，解决方向是连接复用、长连接，而不是盲目调小等待时间。",{"id":339,"title":306,"titles":340,"content":341,"level":302},"\u002Fjava-interview\u002Fnetworking#_2-time_wait-和-close_wait-分别意味着什么",[5],"TIME_WAIT 出现在主动关闭方，是正常的收尾状态，通常持续 2MSL。数量很大往往说明短连接太多，或者服务端主动关闭了连接；优先考虑连接池和 keep-alive。 CLOSE_WAIT 出现在被动关闭方，表示「对方已经发了 FIN，但本端应用还没有调用 close」。大量 CLOSE_WAIT 几乎总是代码问题：连接没有在 finally 中关闭、异常路径漏了释放、连接池配置不当。 排查时用 ss -tan state close-wait 或 netstat 看数量，再结合进程和线程栈定位是哪段代码持有连接。CLOSE_WAIT 不会自己消失，必须由应用关闭。 还要区分 ESTABLISHED 过多（并发高或泄漏）和 SYN_RECV 过多（可能的 SYN Flood）。",{"id":343,"title":90,"titles":344,"content":345,"level":302},"\u002Fjava-interview\u002Fnetworking#_3-tcp-如何保证可靠传输",[5],"每个字节都有序号，接收方用累积确认告诉发送方「我已经收到了哪里」。超时未确认会重传；收到三个重复 ACK 时触发快速重传，不必等到超时。校验和用于发现传输错误。 滑动窗口做流量控制：接收方通过窗口字段告诉发送方还能接收多少，避免发送过快压垮接收端。窗口扩大选项让高带宽高延迟链路也能有足够吞吐。 拥塞控制则根据网络状况调整发送速率，包括慢启动、拥塞避免、快速恢复等阶段。现代内核常用 CUBIC，部分场景用 BBR，它们对丢包和延迟的假设不同。 TCP 保证的是「可靠、有序的字节流」，不保证应用层消息边界，也不保证实时性。UDP 不建立连接、不重传，适合实时音视频、DNS 这类能容忍少量丢包的场景。",{"id":347,"title":106,"titles":348,"content":349,"level":302},"\u002Fjava-interview\u002Fnetworking#_4-粘包拆包是怎么产生的怎么解决",[5],"TCP 是字节流，没有消息边界。发送方两次 write 的数据可能被合并成一个包到达，也可能一条消息被拆成多个包，这就是「粘包」和「拆包」。UDP 有数据报边界，不存在这个问题，但会丢包和乱序。 解决办法是在应用层定义边界：定长消息、分隔符、或者最常用的「长度字段 + 消息体」。长度字段方案最稳妥，能处理任意二进制内容，也是大多数 RPC 协议的选择。 Netty 提供了现成的解码器：LengthFieldBasedFrameDecoder 处理长度前缀，DelimiterBasedFrameDecoder 处理分隔符，FixedLengthFrameDecoder 处理定长。还要配合 IdleStateHandler 处理空闲连接。 注意长度字段的字节序、最大长度限制和半包缓存，恶意超长长度可能造成内存耗尽。",{"id":351,"title":142,"titles":352,"content":353,"level":302},"\u002Fjava-interview\u002Fnetworking#_5-http11http2http3-有什么区别",[5],"HTTP\u002F1.1 支持持久连接，但一个连接同一时刻通常只处理一个请求，浏览器靠开多个连接提升并发；管道化理论上可以连续发请求，但因队头阻塞和代理兼容问题很少使用。 HTTP\u002F2 引入二进制分帧和多路复用，一个 TCP 连接上可以并行多个流，头部用 HPACK 压缩，减少了连接数和头部开销。但 TCP 层仍有队头阻塞：一个包丢失，所有流都要等重传。 HTTP\u002F3 基于 QUIC（UDP），流之间独立，丢包只影响所在流；内置 TLS 1.3，支持 0-RTT 恢复，连接迁移也更平滑。代价是 UDP 在部分网络中被限制，运维和排查方式与 TCP 不同。 选型时还要考虑中间设备和服务端支持，HTTP\u002F2 的收益在高延迟、多资源场景最明显。",{"id":355,"title":158,"titles":356,"content":357,"level":302},"\u002Fjava-interview\u002Fnetworking#_6-https-的握手过程是怎样的",[5],"客户端先发 ClientHello，包含支持的协议版本、加密套件和随机数；服务端回 ServerHello 和证书链。客户端验证证书：是否由受信任 CA 签发、域名是否匹配、是否在有效期内、是否被吊销。 验证通过后，双方通过密钥交换算法协商出共享密钥。现代 TLS 使用 ECDHE，具备前向安全：即使服务器私钥泄露，也无法解密过去的会话。之后用对称加密传输数据，因为对称加密性能更好。 TLS 1.3 把握手压缩到 1-RTT，并支持会话恢复时的 0-RTT，但 0-RTT 数据存在重放风险，只适合幂等请求。 中间人攻击的前提是客户端信任了攻击者的证书，所以证书校验、HSTS 和证书透明度都很重要。HTTPS 保护传输，不保护服务端被入侵后的数据。",{"id":359,"title":174,"titles":360,"content":361,"level":302},"\u002Fjava-interview\u002Fnetworking#_7-cookiesessiontokenjwt-怎么选",[5],"Cookie 是浏览器存储机制，HttpOnly 防止脚本读取，Secure 只走 HTTPS，SameSite 缓解 CSRF。Session 把状态放在服务端，浏览器只存 session id，撤销方便，但需要共享存储。 Token 是广义的凭证，JWT 是其中一种自包含格式：头部、载荷、签名。服务端验签即可，不必查存储，适合水平扩展；但载荷是明文可读的，不能放敏感信息，撤销也需要额外机制。 常见组合是「短期 access token + 长期 refresh token」，敏感操作再校验权限或黑名单。把 JWT 放在 localStorage 容易受 XSS 影响，放在 HttpOnly Cookie 又要防 CSRF，需要配套措施。 选择的核心是状态放在哪、撤销要求多强、是否有跨域和跨端需求，而不是「JWT 更现代」。",{"id":363,"title":209,"titles":364,"content":365,"level":302},"\u002Fjava-interview\u002Fnetworking#_8-netty-为什么高性能零拷贝是什么",[5],"Netty 用主从 Reactor 模型：boss 线程组负责接收连接，worker 线程组负责读写事件；每个 Channel 绑定一个 EventLoop，事件在单线程内串行处理，避免锁竞争。 它基于 NIO 多路复用，支持海量连接；ByteBuf 用读指针和写指针分离读写位置，支持池化和堆外内存，减少分配与复制；Pipeline 用责任链组织编解码和业务处理，扩展方便。 「零拷贝」在不同语境下含义不同：操作系统层面是 sendfile 让数据不经过用户态；Netty 层面更多是 CompositeByteBuf 组合多个缓冲区、FileRegion 传输文件、以及用堆外内存减少 JVM 堆与内核之间的复制。它不等于「完全不拷贝」。 使用时要防止在 EventLoop 里执行阻塞操作，否则会卡住该线程上的所有连接；还要注意 ByteBuf 的引用计数和泄漏检测，堆外内存泄漏不会体现在堆 dump 里。",{"id":367,"title":236,"titles":368,"content":369,"level":302},"\u002Fjava-interview\u002Fnetworking#易错点与自测",[5],"三次握手不是为了「确认双方在线」这么简单，核心是序列号同步和防止旧连接。 TIME_WAIT 是主动关闭方的正常状态，CLOSE_WAIT 堆积多半是应用没关连接。 HTTP\u002F2 解决了 HTTP 层队头阻塞，没有解决 TCP 层；HTTP\u002F3 才换掉 TCP。 自测：① 解释 2MSL 的两个作用；② 说明长度字段解码器如何处理半包；③ 区分 sendfile 零拷贝与 Netty 的缓冲区组合。",{"id":371,"title":263,"titles":372,"content":373,"level":302},"\u002Fjava-interview\u002Fnetworking#参考资料",[5],"RFC 9293：TCP — 连接管理、重传与流量控制的规范 RFC 9110：HTTP 语义 — 方法、状态码与连接语义 RFC 9000：QUIC — HTTP\u002F3 的传输基础 Netty 4.x 用户指南 — Reactor 模型、Pipeline 与编解码器",1793263559963]