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

使用NetClient与JsonParser时仅获首个请求响应的问题求助

嘿,看起来你在Vert.x写TCP客户端的时候碰到了一个常见的坑——发两个请求只收到第一个的响应对吧?我之前也遇到过类似的问题,主要是JsonParser的默认行为和请求响应匹配的问题,下面给你拆解一下怎么解决:

问题分析与修复方案

核心原因

你遇到的问题主要来自两个关键点:

  • JsonParser的一次性解析特性:默认情况下,JsonParser在解析出第一个完整的JSON对象后就会停止监听新的数据流,后续服务器返回的响应自然就被忽略了。
  • 缺少请求-响应的关联机制:发送多个请求时,没有明确的标识来匹配每个请求对应的响应,就算后续有数据回来,也没法把它和对应的请求处理逻辑绑定起来。

具体修复步骤

1. 让JsonParser持续监听数据流

不要只给JsonParser绑定一次性的结束回调,而是用handler方法持续监听,这样每次解析出完整的JSON对象时都会触发处理逻辑。

2. 给每个请求加唯一标识(请求ID)

发送请求时自动生成一个唯一ID,服务器返回响应时也携带这个ID,客户端就能根据ID精准匹配请求和响应,不会搞混。

修改后的完整代码示例

下面是调整后的AsyncClientConnector类,已经修复了上述问题:

package com.luckyrhinos.sbg.robots;

import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetSocket;
import io.vertx.core.parsetools.JsonParser;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;

public class AsyncClientConnector {
    private final Vertx vertx;
    private final NetClient netClient;
    private NetSocket socket;
    // 存储请求ID和对应的响应处理回调
    private final Map<String, Consumer<Object>> responseHandlers = new HashMap<>();

    public AsyncClientConnector(Vertx vertx) {
        this.vertx = vertx;
        this.netClient = vertx.createNetClient();
    }

    public void connect(String host, int port) {
        netClient.connect(port, host, ar -> {
            if (ar.succeeded()) {
                socket = ar.result();
                System.out.println("Connected to server");

                // 初始化持续监听的JsonParser
                JsonParser parser = JsonParser.newParser();
                parser.handler(event -> {
                    if (event.isObject()) {
                        // 从响应中提取请求ID
                        var responseObj = event.object();
                        String requestId = responseObj.getString("requestId");
                        // 找到对应的回调并处理响应
                        if (requestId != null && responseHandlers.containsKey(requestId)) {
                            responseHandlers.get(requestId).accept(responseObj);
                            // 处理完移除回调,避免内存泄漏
                            responseHandlers.remove(requestId);
                        }
                    }
                });

                // 把套接字的输入流持续喂给JsonParser
                socket.handler(buffer -> parser.write(buffer));

                // 处理连接关闭的情况
                socket.closeHandler(v -> {
                    System.out.println("Connection closed");
                    responseHandlers.clear();
                });
            } else {
                System.err.println("Failed to connect: " + ar.cause().getMessage());
            }
        });
    }

    // 封装请求发送逻辑,自动生成请求ID并绑定响应回调
    public void sendRequest(io.vertx.core.json.JsonObject request, Consumer<Object> responseHandler) {
        if (socket == null || !socket.isConnected()) {
            System.err.println("Not connected to server");
            return;
        }
        // 生成唯一请求ID
        String requestId = UUID.randomUUID().toString();
        request.put("requestId", requestId);
        // 存储回调
        responseHandlers.put(requestId, responseHandler);
        // 发送请求(转成Buffer确保服务器能正确解析)
        socket.write(Buffer.buffer(request.encode()));
    }

    public void close() {
        if (socket != null) {
            socket.close();
        }
        netClient.close();
    }

    // 使用示例
    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        AsyncClientConnector connector = new AsyncClientConnector(vertx);

        connector.connect("localhost", 8080);

        // 第一个请求
        var request1 = new io.vertx.core.json.JsonObject()
                .put("action", "getUser")
                .put("userId", 123);
        connector.sendRequest(request1, response -> {
            System.out.println("Received response for request 1: " + response);
        });

        // 第二个请求
        var request2 = new io.vertx.core.json.JsonObject()
                .put("action", "getOrder")
                .put("orderId", 456);
        connector.sendRequest(request2, response -> {
            System.out.println("Received response for request 2: " + response);
        });
    }
}

关键修改说明

  • 持续监听JsonParser:用parser.handler(...)替代一次性解析逻辑,只要有新的完整JSON对象就会触发处理。
  • 请求ID关联机制:通过responseHandlers这个Map来绑定请求ID和回调,收到响应时精准匹配,确保每个请求都能拿到自己的响应。
  • 封装请求发送:sendRequest方法自动生成ID、绑定回调,简化了调用逻辑,不用手动管理ID的问题。

额外注意事项

  • 要确保服务器端也支持请求ID的逻辑,返回响应时必须携带对应的requestId字段,否则客户端没法匹配。
  • 连接关闭时记得清空responseHandlers,避免内存泄漏。
  • 如果服务器返回的是JSON数组,要调整JsonParser的处理逻辑(比如判断event.isArray())。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:35:44