《Java 网络编程基础》6.1 Selector 就绪事件与 interestOps
《Java 网络编程基础》6.1 讲 Selector 的 OP_ACCEPT、OP_READ、OP_WRITE 与 interestOps/readyOps 的区别。用 linkd 的真实探针演示 selectedKeys 不 remove 导致就绪位残留、CancelledKeyException、wakeup 唤醒与 macOS kqueue 空轮询防御,给出单线程事件循环的骨架。
tag
《Java 网络编程基础》6.1 讲 Selector 的 OP_ACCEPT、OP_READ、OP_WRITE 与 interestOps/readyOps 的区别。用 linkd 的真实探针演示 selectedKeys 不 remove 导致就绪位残留、CancelledKeyException、wakeup 唤醒与 macOS kqueue 空轮询防御,给出单线程事件循环的骨架。
FileChannel 能把文件字节送到 socket,但 transferTo 可能只传一部分,非阻塞 channel 还会返回 0。本节用 64 MB 文件在本机 loopback 对比 transferTo 与 64 KB 拷贝循环,讲清 Scatter/Gather、mmap、sendfile 的平台差异,并给出 linkd 静态文件发送的边界。
allocateDirect 把字节放到堆外,不受 -Xmx 管,回收却依赖 Cleaner。本节在 32 MB MaxDirectMemorySize 下复现 Cannot reserve 报错,用 BufferPoolMXBean 观察 10000 个短命缓冲区的延迟释放,并说明 linkd 的 BufferPool 何时该用堆外、何时不该用。
一次 read 之后 position 落在末尾,直接 get 什么也读不到;flip 早了丢数据,flip 两次会重放旧数据。本节用 16 字节缓冲区画出 position、limit、capacity 的每次变化,讲清 flip/clear/compact/rewind 与 slice、只读视图,并给出 linkd 读写切换的排错清单。
帧切出来只是 byte[],还不能说明协议是否正确。本节把 linkd 的 4 字节信封拆成 magic、version、type,再解析后续 payload,用 ProtocolException 区分可恢复与致命错误,给出错误帧、关闭策略和编码解码分离的 FrameEncoder,并完成每连接一份 carry 的 LinkdFrameHandler。
TCP 只保证字节有序,不保证一次 read 等于一条消息。本节实现可运行的定长、分隔符、长度域三种 FrameDecoder,用每 3 字节喂一次的实验复现半包与粘包,讲清长度字段越界、负长度、最大帧限制和返回切片被复用的坑,最后落到 linkd-core 的装配。
字节序与字符集是 linkd 协议的第一道边界。本节用 ByteBuffer 对比大端与小端的真实字节,拆开 UTF-8 和 UTF-16 对汉字、ASCII、emoji 的编码差异,用 CharsetDecoder 的 REPORT 抓住被截断的 3 字节汉字,并给出每连接独占的 Utf8StreamDecoder 与固定大端的 WireInt。
客户端发完请求只是半关闭,服务端要读到 EOF 才能回响应。本节用 linkd 的 Echo 连接实测 shutdownOutput、SO_LINGER(0) 触发的 RST、Connection reset 与 Broken pipe 的差异,并给出停止 accept、排空写队列、半关闭再 close 的优雅关闭顺序。
connect 超时、SO_TIMEOUT 与写阻塞是三件不同的事。本节用 linkd 访问 libraryd 的真实调用,实测 connect 1000ms 超时、read 超时后连接仍可继续读、发送缓冲区写满后线程卡在 SocketDispatcher.write0,并给出 Deadline 总预算与「超时不是重试」的边界。
从 linkd 的最小阻塞 Echo 入口出发,拆开 ServerSocket 的 bind、listen、accept 与 Socket 的读写阻塞点,用本机实测说明 backlog、SO_REUSEADDR 与 TIME_WAIT 重绑行为,给出可停止的 Echo 服务器、半包边界和 netstat 诊断命令。
InetAddress 解析可能返回多个地址,localhost 同时是 127.0.0.1 和 ::1。本节观察 getAllByName、IPv4/IPv6 回环、端口 0 的临时端口分配、重复绑定的 BindException 原文,以及 lo0 MTU 16384 与 en0 MTU 1500,并用 netstat/lsof 验证,最后落到 LinkdMain 的绑定日志。
UDP 一次 send 就是一条消息,但消息可能丢、可能被截断。本节用 DatagramSocket/DatagramPacket 实测数据报边界、100 字节缓冲接收 2000 字节报文时的静默截断、70000 字节报文的 Message too long,以及 4096 字节接收缓冲下 5000 个包只收到 3 个的丢包结果,并给出 linkd 指标上报的 UDP 边界。
linkd 的 HTTP 响应在 loopback 上被拆成多次 read。本节用最小程序观察三次握手在 Java 里的可见时刻、两次 write 被合并或拆开的真实输出、Nagle 与 setTcpNoDelay 的测量,以及 SO_LINGER(0) 触发 RST 和 TIME_WAIT 的 netstat 证据,最后落到 Connection 与 FrameDecoder 的边界处理。
把 linkd 拆成 core、http、ws、app 四个 Maven 模块,固定 JDK 25.0.5 与 9090/8080 端口约定;给出 JAVA_HOME 设置、父 POM、启动与 curl/lsof/netstat 验证步骤,并记录 macOS 没有 ss/epoll 时的替代诊断命令。
阻塞 Echo 与非阻塞 Selector Echo 各跑一遍,用线程栈、read 返回值和事件日志对比两种模型;再拆开 BIO、NIO、AIO 与 Reactor 的边界,说明 linkd 为什么用单线程事件循环加业务线程池,而不是给每个连接开一个线程。
一次 curl 请求打到 127.0.0.1:9090/healthz,中间经过内核监听队列、收发缓冲区、Selector 就绪事件、业务线程和响应写回。本节用 curl -v、lsof、netstat 与本机服务日志把这条路径逐层标出来,并说明 linkd 的 EventLoop、Handler 和 Connection 分别落在哪一层。
《Java 网络编程基础》全 50 篇的目录与学习路径:15 章 45 节,从请求路径、Socket、NIO Buffer 与 Selector,到手写 HTTP/1.1、HTTP/2、TLS 与 WebSocket,最后以 linkd v1 验收收口;给出按目标、按症状、按项目三条路线,以及各卷衔接、项目状态与实验口径。
交代本书的读者对象与前置知识,列明 JDK 25.0.5+8、Maven 3.9.12 与 macOS 实验环境,说明卷一不引入第三方网络库的约束、代码与输出标注约定、四层验证方法,以及与 network 专题、MMO 网络卷、Spring Boot 三部曲和 JVM 卷的内容边界。
说明 Java 四卷本之后为什么还要单独写网络栈:从一次请求的真实路径讲到 linkd 网关,交代读者画像、三卷路线、与站内 network 专题和 MMO 网络卷的边界,以及先跑后改、每节落到 linkd 具体类的学习方式与实验纪律,并说明本书不写什么。
《Java 虚拟机原理与性能调优》全 31 篇,承接卷三 libraryd,用 jcmd、JFR、GC 日志与堆转储讲清类加载、对象布局、JIT、垃圾回收、内存诊断、线程锁运行时、启动加速与综合压测调优:10 章 30 节加目录的完整索引。