Java ByteBuffer 读取不完整时怎样正确使用 flip 和 compact
Java NIO 的分段读取不能把一次 read 当成一条完整消息。正确做法是:写入完成后用 flip() 把 position/limit 切到读取模式;解析到不完整消息时,把 position 恢复到这条消息的起点;本轮结束调用 compact(),让未消费字节回到缓冲区开头,下一次读取接在它后面。只有已经消费完的数据才应该被覆盖,不能用 clear() 代替保留动作。
flip()把“已经写入的范围”变成“本轮可读范围”,不是清空数据。- 解析不完整帧时要保留整帧起点,不能只保留已经读出的长度字段。
compact()之后 position 等于未消费字节数,下一次read会从该位置继续写入。
先把三个位置信息分清楚
ByteBuffer 同时承担读和写两个角色。接收数据前通常处于写入模式:position 指向下一个可写位置,limit 等于 capacity。调用 flip() 后,limit 被改成原来的 position,position 归零,于是 0 到 limit 之间就是本轮收到的数据。
| 状态 | position | limit | 下一步 |
|---|---|---|---|
| 写入模式 | 下一个写入位置 | capacity | Channel.read(buf) |
| 读取模式 | 下一个消费位置 | 本轮已写入末尾 | 解析或写出 |
| compact 后 | 未消费字节数 | capacity | 继续 read |
例如容量为 1024,本轮读到 37 字节,flip() 后就是 position=0、limit=37;如果解析只消费了 12 字节,剩下 25 字节仍是有效输入。

解析不完整消息时保留整条边界
假设协议头部包含 4 字节长度,后面跟消息体。解析器不能先把长度字段取走,发现消息体不够后再等待,因为这样下一轮已经失去当前帧的起点。更稳妥的方式是先保存 frameStart,确认头部和完整消息都在当前可读范围内后再推进 position。
import java.nio.ByteBuffer;
// 读取一轮数据;返回 false 表示遇到 EOF,true 表示本轮可继续。
static boolean readAndParse(java.nio.channels.ReadableByteChannel channel,
ByteBuffer buffer) throws java.io.IOException {
int n = channel.read(buffer); // 写入模式:新数据追加到 position
if (n == -1) {
return buffer.position() == 0; // 没有保留数据才算正常结束
}
buffer.flip(); // 将本轮写入范围切换成可读范围
while (buffer.hasRemaining()) {
int frameStart = buffer.position(); // 不完整时回到整帧起点
if (buffer.remaining() buffer.capacity() - Integer.BYTES) {
throw new java.io.IOException("消息长度超出缓冲区边界");
}
if (buffer.remaining()
这里的关键不是“读到多少就处理多少”,而是让解析器只提交完整帧。遇到半个头部或半个消息体时,恢复到 frameStart,随后 compact() 会把整段未消费内容搬到索引 0。
flip、compact 和 clear 的调用边界
一轮读取的顺序可以固定为“read → flip → parse → compact → read”。clear() 只适用于确认缓冲区中没有需要保留的数据;它会把 position 置零并把 limit 恢复为 capacity,但不会真的擦除字节。对半帧调用 clear,下一轮就无法补齐原消息。
如果本轮恰好消费完所有数据,解析结束时 position 等于 limit,compact() 会留下 0 个字节,下一次读取自然从索引 0 开始。如果只消费了部分数据,compact 后 position 就是剩余长度,不能再额外调用 flip 后直接继续写入,否则会把写入位置切回错误的范围。

生产环境的检查清单
- 每次成功写入后只 flip 一次;解析循环内部不要重复 flip。
- 协议长度必须有上限,防止异常输入让缓冲区永远等待或触发过大分配。
read()返回 0 时不要当作 EOF;非阻塞通道应回到选择器等待可读事件。- 收到 -1 时先判断 compact 后是否还留有半帧,再决定记录截断并关闭连接。
- 只在完整帧提交后推进外层 position,异常路径要关闭通道并释放连接状态。
相关问题
为什么 flip 后不能直接继续 read?
flip 后是读取模式,read 会把数据写入 position 到 limit 的范围;要继续接收,必须先完成解析并 compact,恢复可写空间。
compact 会不会丢掉已经读过的字节?
会。它只复制 position 到 limit 的未消费区,已经消费的字节不会保留,这正是下一轮避免重复解析所需要的行为。
半帧一定要扩大 ByteBuffer 吗?
不一定。先设置协议最大帧长;未超过容量时等待下一轮并 compact,超过上限则拒绝消息或切换到专门的分片存储策略。
Java Bidi 怎么让段落方向跟随首个强方向字符
- 上一篇
- Java Bidi 怎么让段落方向跟随首个强方向字符
- 下一篇
- Java BreakIterator 怎样按本地化规则拆分用户可见字符
-
- 文章 · java教程 | 30分钟前 |
- Java Bidi 怎么让段落方向跟随首个强方向字符
- 123浏览 收藏
-
- 文章 · java教程 | 17小时前 |
- Java NumberFormat 怎样限制只解析输入中的整数部分
- 218浏览 收藏
-
- 文章 · java教程 | 18小时前 | java nfc unicode Normalizer isNormalized
- Java Normalizer 怎样检查输入文本是否已经采用 NFC
- 182浏览 收藏
-
- 文章 · java教程 | 18小时前 | java 双向文本 BiDi 视觉区段 reorderVisually
- Java Bidi 怎样遍历混合方向文本的视觉区段
- 137浏览 收藏
-
- 文章 · java教程 | 19小时前 | java 数字格式化 NumberFormat FieldPosition INTEGER_FIELD
- Java FieldPosition 怎样定位数字格式化后的整数部分
- 307浏览 收藏
-
- 文章 · java教程 | 19小时前 | Java ·
- Java ParsePosition 怎样报告日期字符串解析失败的位置
- 428浏览 收藏
-
- 文章 · java教程 | 20小时前 | Java · 格式化 · 浮点数 · java NumberFormat ChoiceFormat nextDouble 浮点阈值
- Java ChoiceFormat 怎样用 nextDouble 设置开区间阈值
- 311浏览 收藏
-
- 文章 · java教程 | 22小时前 | 占位符 · java 国际化 单引号 占位符 messageformat
- Java MessageFormat 中单引号为什么会吞掉占位符
- 345浏览 收藏
-
- 文章 · java教程 | 22小时前 |
- Java DateFormatSymbols 怎样读取指定 Locale 的月份名称
- 411浏览 收藏
-
- 文章 · java教程 | 23小时前 |
- Java Collator 怎样设置重音与大小写的比较层级
- 190浏览 收藏
-
- 文章 · java教程 | 23小时前 |
- Java Collator 怎样用 CollationKey 加速重复排序
- 309浏览 收藏
-
- 文章 · java教程 | 1天前 | java UTF-16 字素簇 BreakIterator
- Java BreakIterator 的字符边界为什么不同于 char 下标
- 273浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 977次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 983次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1098次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1070次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 908次使用
-
- Go Java 算法之字符串解码示例详解
- 2023-01-07 479浏览
-
- Go Java算法之单词搜索示例详解
- 2022-12-30 337浏览
-
- Gojava算法之括号生成示例详解
- 2023-02-22 128浏览
-
- GoJava算法之累加数示例详解
- 2023-01-07 149浏览
-
- GoJava算法最大单词长度乘积示例详解
- 2023-01-12 202浏览
