You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何解析Socket接收的单个字符串内多个JSON对象

解决Socket粘包导致的连续JSON解析问题

Socket的缓冲机制(如Nagle算法)会将多个小消息合并发送,导致接收端拿到多个JSON对象拼接成的非法字符串,无法直接解析。以下是几种实用的解决方法:

方法一:发送端添加消息分隔符

在每个JSON消息末尾添加一个不会出现在JSON内容中的分隔符(比如换行符\n、特殊字符串###END###),接收端按分隔符拆分后逐个解析。

发送端示例(Java)

String jsonMsg = "{\"action\":\"ping\",\"time\":1684610209993}";
// 每个JSON后追加换行符
outputStream.write((jsonMsg + "\n").getBytes(StandardCharsets.UTF_8));

接收端处理逻辑

读取数据时,按分隔符拆分缓冲区内容,将每个独立的JSON字符串取出解析:

BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
    // 解析单个JSON字符串
    PingAction action = new ObjectMapper().readValue(line, PingAction.class);
    // 处理业务逻辑
}

方法二:使用长度前缀标记消息

发送前先传递当前JSON消息的字节长度,接收端先读取长度值,再读取对应长度的字节内容,精准拆分每个消息。这种方法不会出现分隔符冲突的问题,适合复杂场景。

发送端示例(Java)

String jsonMsg = "{\"action\":\"ping\",\"time\":1684610209993}";
byte[] jsonBytes = jsonMsg.getBytes(StandardCharsets.UTF_8);

// 先发送4字节的长度(大端字节序,接收端要统一)
ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
lengthBuffer.putInt(jsonBytes.length);
outputStream.write(lengthBuffer.array());

// 再发送JSON内容
outputStream.write(jsonBytes);

接收端处理逻辑

// 先读取4字节的长度
byte[] lengthBytes = new byte[4];
inputStream.read(lengthBytes);
int msgLength = ByteBuffer.wrap(lengthBytes).getInt();

// 读取对应长度的JSON内容
byte[] jsonBytes = new byte[msgLength];
inputStream.read(jsonBytes);
String jsonMsg = new String(jsonBytes, StandardCharsets.UTF_8);

// 解析JSON
PingAction action = new ObjectMapper().readValue(jsonMsg, PingAction.class);

方法三:接收端流式解析(无需修改发送端)

如果无法修改发送端的消息格式,可使用JSON流式解析器(如Jackson的JsonParser、Gson的JsonReader)直接从粘包字符串中逐个提取JSON对象,无需提前拆分。

示例(Jackson流式解析)

String stickyJson = "{\"action\":\"ping\",\"time\":1684610209993}{\"action\":\"ping\",\"time\":1684610210225}";

JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(stickyJson)) {
    JsonToken token;
    while ((token = parser.nextToken()) != null) {
        // 遇到JSON对象开始标记时,读取整个对象
        if (JsonToken.START_OBJECT.equals(token)) {
            PingAction action = parser.readValueAs(PingAction.class);
            // 处理单个PingAction对象
            System.out.println("Ping time: " + action.getTime());
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 对应的实体类
static class PingAction {
    private String action;
    private long time;

    // Getter & Setter
    public String getAction() { return action; }
    public void setAction(String action) { this.action = action; }
    public long getTime() { return time; }
    public void setTime(long time) { this.time = time; }
}

方案选型建议

  • 优先选择方法一或方法二:从发送端规范消息格式,从根源避免粘包问题,逻辑更清晰可靠。
  • 若无法修改发送端,再使用方法三:通过流式解析绕过粘包拆分的问题,适合兼容旧系统的场景。

内容的提问来源于stack exchange,提问作者Ebrahim Karimi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 05:00:39