使用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
相关产品推荐
相关产品推荐

