SocketChannel.shutdownOutput()向对端发送FIN,启动TCP半关闭流程:仅终止本端写入能力,保留读取能力,不关闭通道本身,输入流仍可正常read()。

SocketChannel.shutdownOutput() 不是简单地“关掉输出”,而是向对端发送 FIN,启动 TCP 半关闭流程——只终止本端写入能力,保留读取能力。它不关闭通道本身,也不影响后续从输入流读取数据。
shutdownOutput() 的真实行为
调用该方法后:
- 内核立即把发送缓冲区中剩余数据发完,然后发送一个 FIN 包给对端
- 本端 SocketChannel 的输出流不可再写,再次 write() 会抛出 ClosedChannelException
- 通道本身仍处于 open 状态,输入流可继续 read(),直到对端也关闭或连接真正断开
- 不会触发四次挥手中的“主动关闭方”完整流程(即不进入 FIN_WAIT1 → FIN_WAIT2 → TIME_WAIT),只完成前半段挥手
它在四次挥手中的位置与作用
TCP 四次挥手本质是双向独立关闭。shutdownOutput() 对应的是其中“一方先关闭写方向”的动作:
- 当服务端调用 shutdownOutput(),它发出 FIN,客户端收到后进入 CLOSE_WAIT 状态
- 此时服务端仍可 read() 客户端可能发来的剩余数据(比如响应确认、错误信息等)
- 客户端若也调用 shutdownOutput() 或 close(),才完成另一次 FIN 发送,走向最终断开
- 因此,它把原本原子的“close() = 关读+关写+发FIN+释放资源”,拆解为可控的两步
和 close() 的关键区别
两者语义和效果完全不同:
- close():彻底释放通道资源,同时关闭读写两端,无法再 read() 或 write(),且会丢弃接收缓冲区中尚未处理的数据
- shutdownOutput():仅关闭写方向,不释放通道,不丢接收缓存数据,read() 仍有效;适合“我发完了,但还要听你回什么”的场景
- 典型误用:在 NIO Selector 循环中对已 shutdownOutput() 的 channel 仍反复注册 OP_READ —— 这会导致持续就绪,因为 EOF(read 返回 -1)还没到来,需配合对端行为判断是否真正结束
实际应用建议
常见于需要“单向通知完成”的协议交互:
- 文件上传完成时,客户端 shutdownOutput() 告知服务端“数据发完了”,服务端校验后返回结果,客户端再 read() 获取响应
- HTTP/1.0 中无 Content-Length 且使用 Connection: close 时,服务端用 shutdownOutput() 表示响应体结束,同时保持连接接收客户端可能的下个请求(虽不推荐,但协议允许)
- 避免在未读尽对端数据前就 close(),否则可能丢失最后几字节;shutdownOutput() + 循环 read() 直到 -1 是更安全的收尾组合

















