TCP 流通信中的 EOFException 与 JSON 半包障碍解析

news/2026/1/22 22:08:28/文章来源:https://www.cnblogs.com/yangykaifa/p/19519154

一、背景

在做机器人与上位机通信调试时,我遇到一个典型的异常:

Caused by: java.io.EOFException: End of input at line 1 column 2697 path $.results.charge_point_3F_1.pose.orientation.y

表面看似 JSON 格式错误,实际上是——收到的 JSON 不完整(半包)

二、问题重现

在 Android 端通过 TCP 接收上位机发来的 JSON 数据,直接用 Gson 解析:

val json = String(bytes, Charsets.UTF_8)
val obj = Gson().fromJson(json, RobotMarkers::class.java)

第一段刚好停在 "y": 后面。
Gson 在解析时发现缺少值和右括号,于是抛出 EOFException。


三、TCP 是流,不是消息队列

这其实是一个经典误区:

“一次 read() 读取的数据,就是一条完整消息。”

错!

TCP 是“流式传输”,它只保证字节顺序,不保证消息边界。
因此可能出现:

情况描述示例
半包一条消息被拆成多次到达{ "a":1, "b": + 2 }
粘包多条消息连在一起{ "a":1 }{ "b":2 }

四、为什么会抛 EOFException?

Gson.fromJson() 会逐字符读取输入流:

  • 它期望 JSON 结构完整闭合({} 或 [])。

  • 当还没读完对象,却到达字符串结尾(EOF)→ 抛异常。

也就是说:

EOFException = “输入流已结束,但我还没看到该有的 } 或 ]。”


五、解决方案

✅ 方案一:协议层加“定界符”

最根本的办法是:

让每条消息“有边界”。

常见两种协议形式:

1. 长度前缀

发送端先写入 4 字节长度,再写入 JSON 内容。

[4字节长度][JSON字节...]

接收端先读长度,再循环读取指定字节数 → 这就是完整 JSON。

2. 行分隔(NDJSON)

每条 JSON 以换行 \n 结尾。
接收端用 BufferedReader.readLine() 读取一整行后解析。


✅ 方案二:客户端做“拼包器”

如果短期内无法改协议,可以在客户端拼接:

class JsonFramer {private val sb = StringBuilder()fun feed(chunk: String): List {sb.append(chunk)val out = mutableListOf()var i = 0; var depth = 0; var inStr = false; var esc = false; var start = -1while (i < sb.length) {val c = sb[i]if (inStr) {if (esc) esc = false else if (c == '\\') esc = true else if (c == '"') inStr = false} else when (c) {'"' -> inStr = true'{' -> if (depth++ == 0) start = i'}' -> if (--depth == 0 && start >= 0) {out += sb.substring(start, i + 1)start = -1}}i++}if (out.isNotEmpty()) sb.delete(0, sb.lastIndexOf('}') + 1)return out}
}

每次 read() 到的数据都 feed() 一次,
feed() 会在 JSON 结构完整时返回一条完整字符串。

✅ 方案三:解析前清洗数据

有时候日志里包含前缀,比如:

收到 TCP 消息: {...}

要裁掉这些内容:

fun clean(raw: String): String {val start = raw.indexOf('{')val end = raw.lastIndexOf('}')return raw.substring(start, end + 1)
}

六、lenient 模式能解决吗?

JsonReader.setLenient(true) 只是在语法层放宽规则:

  • 允许单引号、注释、NaN 等;

  • ❌ 不会修复“半包”或“粘包”问题

七、最佳实践:稳健 TCP 接收逻辑

val buf = ByteArray(16 * 1024)
val framer = JsonFramer()
while (true) {val n = input.read(buf)if (n == -1) breakval chunk = String(buf, 0, n, Charsets.UTF_8)val frames = framer.feed(chunk)for (json in frames) {val clean = clean(json)val obj = Gson().fromJson(clean, Response::class.java)process(obj)}
}

八、常见排查 Checklist ✅

检查项说明
是否分多次 read()打印每次字节数确认
是否有日志前缀清洗字符串
缓冲大小是否过小建议 ≥ 16KB
是否拼包必须有!
是否多条粘在一起用 Framer 逐条拆分
是否 lenient可用于调试,但不治本

九、总结

核心问题原因解决方案
EOFExceptionJSON 被截断(半包)拼包或协议定界
MalformedJsonException粘包或脏前缀拆包 + 清洗
偶发成功偶发失败TCP 分段不固定循环读 + 拼包

⚙️ 记住一句话:
TCP 是流,JSON 是消息。要想稳定,必须“定界”。

十、结语

EOFException 并不可怕,
可怕的是我们误以为“读一次就是一条消息”。

在任何基于 TCP 的 JSON 通信中,都应该建立一层消息边界感知机制。
只有这样,通信层才真正做到“稳如老狗”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1201897.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亲测有效:10个免费降AI工具+完整操作流程,成功将AIGC风险降至10%

上个月&#xff0c;室友在提交论文时&#xff0c;用检测系统一查&#xff0c;结果显示“AI率87%”。这个数字在只剩几天就要二次提交的情况下&#xff0c;确实会让人非常焦虑。 为了解决这个问题&#xff0c;我花时间把市面上能找到的降ai率工具都试了一遍&#xff0c;前后测试…

吉时利6517B 静电计/高阻表: 高精度电学测量的专业选择

156/2558/3328 产品概述&#xff1a;在电学测量领域&#xff0c;尤其是微弱电流与高阻抗测量中&#xff0c;仪器的精度、灵敏度与稳定性直接决定了科研与工业检测的可靠性。吉时利&#xff08;Keithley&#xff09;作为精密测量仪器领域的全球领导者&#xff0c;其推出的6517B静…

2026毕业生必备:用这10款降AI工具和免费降AI方法,高效降低论文AI率至10%

上个月&#xff0c;室友在提交论文时&#xff0c;用检测系统一查&#xff0c;结果显示“AI率87%”。这个数字在只剩几天就要二次提交的情况下&#xff0c;确实会让人非常焦虑。 为了解决这个问题&#xff0c;我花时间把市面上能找到的降ai率工具都试了一遍&#xff0c;前后测试…

KEYSIGHT是德 N1912A功率计:宽带多通道功率测量的标杆之选

156/2558/3328产品概述&#xff1a;在无线通信、雷达系统、半导体测试等高速发展的技术领域&#xff0c;对射频微波功率进行精确、快速、可靠的测量至关重要。作为测试测量行业的领导者&#xff0c;是德科技&#xff08;Keysight Technologies&#xff09;推出的N1912A双通道功…

Java毕设选题推荐:基于springboot的闲一品闲置品交易平台【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

搜维尔科技:【工业前沿——Tesollo】机器人工匠打造“点石成金”机械手

无论机械臂多么精密复杂,如果“手”的功能不正常,一切都将徒劳无功。对于要在工业现场执行实际工作的机器人而言,“手”的功能—抓取、转动和组装物体—与移动能力同样至关重要。 图片 Tesollo是一家专注于研发堪称…

向kafka写入数据

向kafka写入数据import json,os,ast from typing import Any import logging from kafka import KafkaProducer from kafka.errors import KafkaError, NoBrokersAvailable from typing import Dict, List, Optional# …

免费且高效:10个降AI工具深度测评+降AI方法使用方案,AI率轻松降到10%

上个月&#xff0c;室友在提交论文时&#xff0c;用检测系统一查&#xff0c;结果显示“AI率87%”。这个数字在只剩几天就要二次提交的情况下&#xff0c;确实会让人非常焦虑。 为了解决这个问题&#xff0c;我花时间把市面上能找到的降ai率工具都试了一遍&#xff0c;前后测试…

21.BeanFactory 和 ApplicationContext 有什么区别

BeanFactory 和 ApplicationContext 有什么区别&#xff1f; BeanFactory 可以理解为含有 bean 集合的工厂类。BeanFactory 包含了种 bean 的定义&#xff0c;以便在接收到客户端请求时将对应的 bean 实例化。 BeanFactory 还能在实例化对象的时生成协作类之间的关系。此举将…

【计算机毕业设计案例】基于SpringBoot的野生动物园财务与票务一体化平台基于springboot的西安秦岭野生动物园智能化管理系统(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

【计算机毕业设计案例】基于springboot的闲一品闲置品交易平台于Java+SpringBoot的闲置用品交易平台(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

牛顿插值(测试)

N(x) f[x0] f[x0,x1](x-x0) f[x0,x1,x2](x-x0)(x-x1) ... f[x0,...,xn](x-x0)...(x-x_{n-1}) 其中 f[x0,...,xk] 是 k 阶差商。 差商表可视化示例: 假设&#xff1a;对于点 (1,1), (2,4), (3,9)&#xff0c;(4,16) 差商表:索引 x yf(x) 一阶差商 二阶差商 三阶…

计算机Java毕设实战-基于springboot的闲一品闲置品交易平台基于SpringBoot的闲置物品交易系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

【计算机毕业设计案例】基于springboot的社区协作与资源共享系统基于springboot+vue的社区资源共享系统设计与实现(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

A.每日一题——3507. 移除最小数对使数组有序 I

题目链接&#xff1a;3507. 移除最小数对使数组有序 I&#xff08;简单&#xff09; 算法原理&#xff1a; 解法&#xff1a;模拟 3ms击败55.74% 时间复杂度O(N) 由于数据范围非常小&#xff0c;所以直接按题意模拟即可 将nums的所有相邻元素全部扔进顺序表里&#xff0c;方便修…

计算机Java毕设实战-基于springboot的社区协作与资源共享系统社区闲置资源交易与共享系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

18.Spring中用到了什么设计模式

Spring 框架在其设计和实现中广泛应用了多种设计模式&#xff0c;其中一些关键的设计模式包括如下&#xff1a;工厂模式&#xff08;Factory Pattern&#xff09;&#xff1a;BeanFactory 和 ApplicationContext&#xff1a;Spring 使用工厂模式来管理对象的创建和装配&#xf…

Java毕设项目推荐-基于springboot的新闻时事热点日报管理系统设计与实现【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

Java毕设项目推荐-基于springboot的饰品销售商城系统基于web的饰品商城的设计与实现【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

使用clickhouse-connect库并指定列类型进行插入到clickhouse中

使用clickhouse-connect库并指定列类型进行插入到clickhouse中以前一直自己构造sql语句进行写入,现在使用参数化插入/orm进行写入,比较方便的是,无需理会一些乱七八糟的符号问题导致的插入失败.#!/usr/bin/python3 # -…