Java Process调用curl首次迭代返回退出码35问题排查
Java调用curl首次执行TLS连接错误(退出码35)分析与修复
问题场景
循环调用Java方法通过Process执行curl向API发送JSON数据时,后续所有更新均成功,但首次执行失败,返回curl退出码35(属于TLS/SSL连接类错误)。核心代码如下:
File f = new File("c:\\temp\\json.txt"); String str = "{\"batch_fields\":{\"Batch Number\":\""+num+"\"}}"; try(BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { writer.write(str); } StringBuilder sb = new StringBuilder(); StringBuilder command = new StringBuilder("c:\\Program Files\\curl\\bin\\curl -H \"X-Token: "+BatchUtils.API_TOKEN_READ_WRITE+"\""); command.append(" -X PUT -H \"Content-Type: application/json\" "); command.append(" -d \"@c:\\temp\\json.txt\" "); command.append("https://blah/blah/batches/"+batchId+"\""); ProcessBuilder processBuilder = new ProcessBuilder(command.toString().split(" ")); Process process = processBuilder.start(); InputStream is = process.getInputStream(); if(is == null || is.read() == -1) { is = process.getErrorStream(); sb.append(" Failed updateBatchNumber: " + is.toString()); } else sb.append(" Updated batch number"); int exitCode = process.waitFor(); sb.append(". exit code: " + exitCode); process.destroy();
错误原因分析
- 命令拆分逻辑错误:使用
split(" ")拆分带空格的curl路径(c:\\Program Files\\curl\\bin\\curl)会导致路径被拆分为c:\\Program和Files\\curl\\bin\\curl两个无效参数,首次执行时无法找到正确的curl可执行文件,触发SSL相关错误(实际是命令执行失败的间接表现)。后续执行可能因系统文件缓存或偶然因素规避了该问题,但这是代码的根本性缺陷。 - URL末尾多余引号:代码中URL拼接时末尾多了一个双引号(
command.append("https://blah/blah/batches/"+batchId+"\"")),导致curl解析URL时出现格式错误,加剧连接失败概率。 - 错误流读取不完整:仅通过
is.read() == -1判断输入流为空,且直接输出is.toString()无法获取curl的详细错误日志,无法定位具体TLS握手失败原因(如证书信任、握手超时、TLS版本不兼容等)。 - 首次TLS握手开销:首次建立TLS连接时需要完成证书验证、密钥交换等操作,若API端响应较慢或本地SSL缓存未初始化,可能触发超时,但这并非核心原因,代码本身的命令构建错误才是主要诱因。
修复方案
1. 正确构建ProcessBuilder命令列表
避免使用字符串拆分,直接传入参数列表,处理带空格的路径:
List<String> command = new ArrayList<>(); command.add("c:\\Program Files\\curl\\bin\\curl"); command.add("-H"); command.add("X-Token: " + BatchUtils.API_TOKEN_READ_WRITE); command.add("-X"); command.add("PUT"); command.add("-H"); command.add("Content-Type: application/json"); command.add("-d"); command.add("@c:\\temp\\json.txt"); command.add("https://blah/blah/batches/" + batchId); ProcessBuilder processBuilder = new ProcessBuilder(command);
2. 完整读取错误输出
替换原有的流读取逻辑,获取curl的详细错误信息,用于排查问题:
// 读取输入流和错误流 String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); String errorOutput = new String(process.getErrorStream().readAllBytes(), StandardCharsets.UTF_8); int exitCode = process.waitFor(); if (exitCode != 0) { sb.append(" Failed updateBatchNumber: ").append(errorOutput); } else { sb.append(" Updated batch number: ").append(output); } sb.append(". exit code: ").append(exitCode);
3. 优化curl的TLS连接参数
针对首次TLS握手问题,可添加curl参数优化连接:
- 添加
--connect-timeout 10设置连接超时时间(单位:秒) - 添加
--tls-max 1.2指定兼容的TLS版本(根据API支持情况调整) - 添加
--insecure临时跳过证书验证(仅测试环境使用,生产环境禁用)
示例:
command.add("--connect-timeout"); command.add("10"); command.add("--tls-max"); command.add("1.2");
4. 确保文件写入完成
虽然try-with-resources会自动关闭writer,但可显式调用flush()确保内容写入磁盘后再执行curl:
try(BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { writer.write(str); writer.flush(); // 确保内容写入磁盘 }
内容的提问来源于stack exchange,提问作者mrcrag
相关产品推荐
相关产品推荐

