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

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();

错误原因分析

  1. 命令拆分逻辑错误:使用split(" ")拆分带空格的curl路径(c:\\Program Files\\curl\\bin\\curl)会导致路径被拆分为c:\\Program和Files\\curl\\bin\\curl两个无效参数,首次执行时无法找到正确的curl可执行文件,触发SSL相关错误(实际是命令执行失败的间接表现)。后续执行可能因系统文件缓存或偶然因素规避了该问题,但这是代码的根本性缺陷。
  2. URL末尾多余引号:代码中URL拼接时末尾多了一个双引号(command.append("https://blah/blah/batches/"+batchId+"\"")),导致curl解析URL时出现格式错误,加剧连接失败概率。
  3. 错误流读取不完整:仅通过is.read() == -1判断输入流为空,且直接输出is.toString()无法获取curl的详细错误日志,无法定位具体TLS握手失败原因(如证书信任、握手超时、TLS版本不兼容等)。
  4. 首次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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:44:51