Java Process控制台写入后关闭,再发命令报Stream closed错误求助
解决Process控制台写入后关闭流导致后续命令报错的问题
我来帮你捋清楚这个问题——你遇到的核心矛盾是Process关联的标准输入流一旦关闭,就再也没法向子进程写入命令了,而你之前的操作逻辑是写完一次就关流,这自然会导致后续发送命令时出现“Stream closed”错误。
问题根源拆解
当你调用getOutputStream().close()时,本质是告诉子进程:“我没有更多输入要发送了”,子进程的stdin会被标记为已关闭,后续再尝试写入肯定会报错。而且从你的代码片段来看,你用了cmd /c start && cmd.exe这种写法,其实start会新开一个独立的cmd窗口,和当前Process对象完全脱离,这会导致你后续的输入流根本没关联到真正运行服务器的那个会话上,也是问题的诱因之一。
解决方案:保持输入流打开,用刷新替代关闭
下面是针对你的场景修改后的代码,核心是不关闭输入流,用flush()提交命令,同时正确构造交互式的cmd会话:
public void runServer(String servername) throws IOException, InterruptedException { // 直接启动交互式cmd,不要加/c参数(/c会执行完命令就关闭cmd) ProcessBuilder builder = new ProcessBuilder("cmd.exe"); // 把错误流合并到输出流,方便统一读取排查问题 builder.redirectErrorStream(true); Process process = builder.start(); // 用BufferedWriter封装输入流,方便写入命令 BufferedWriter cmdWriter = new BufferedWriter( new OutputStreamWriter(process.getOutputStream()) ); // 第一步:切换到服务器目录,命令结尾必须加换行(cmd靠换行识别命令结束) cmdWriter.write("cd \"" + cm.getDataPath("server") + "\\" + servername + "\""); cmdWriter.newLine(); cmdWriter.flush(); // 立即把命令发送给子进程,不要等缓冲区满 // 第二步:启动服务器jar包 cmdWriter.write("java -jar \"" + cm.getDataPath("main") + "\\server.jar\""); // 替换成你的实际jar文件名 cmdWriter.newLine(); cmdWriter.flush(); // 👉 重点:这里不要关闭cmdWriter!保持流打开,后续还能发送新命令 // cmdWriter.close(); ❌ 绝对不能在这里关 // 可选:启动单独线程读取子进程的输出,避免子进程因输出缓冲区满而挂起 new Thread(() -> { try (BufferedReader outputReader = new BufferedReader( new InputStreamReader(process.getInputStream()) )) { String line; while ((line = outputReader.readLine()) != null) { System.out.println("服务器输出:" + line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 如果需要等待服务器进程结束,再调用waitFor() // process.waitFor(); // 当你确定不再需要发送命令时,再关闭流并销毁进程 // cmdWriter.close(); // process.destroy(); }
关键注意事项
- 不要用
cmd /c启动:/c参数会让cmd执行完指定命令后直接退出,没法保持交互式会话,也就没法发送后续命令。 - 命令必须加换行符:cmd是按换行来判断输入结束的,只写命令不换行,子进程会一直等待输入完成。
- 用flush()替代close()提交命令:flush会把缓冲区的内容立即发送给子进程,同时保持流的打开状态,后续可以继续写入新命令。
- 单独线程处理输出:子进程的输出如果不及时读取,会填满操作系统的输出缓冲区,导致子进程挂起,所以一定要用单独线程来读取输出流。
内容的提问来源于stack exchange,提问作者S3nS3IW00
相关产品推荐
相关产品推荐

