如何在Java应用中持续读取批处理命令(tshark)的输出?
解决Java中持续读取Process输出的停顿问题
首先得指出一个小细节:你提到用process.getOutputStream()获取返回值,这里应该是笔误吧?getOutputStream()是用来写入子进程的输入流,要读取子进程的输出,正确的方法是用getInputStream(),而错误输出需要用getErrorStream()——这个误区很关键,因为如果错误输出的缓冲区满了,也会导致子进程阻塞,进而出现你看到的停顿现象。
为什么会出现打印停顿?
tshark持续输出数据时,Java的Process会把输出暂存在系统缓冲区里(默认大小大概对应1200行左右的内容),当缓冲区被填满后,tshark会暂停输出,直到Java程序读取并清空缓冲区。你遇到的每1200行停10秒,就是因为主线程没有及时读取缓冲区,或者在处理输出时被其他逻辑阻塞了。
具体解决方案
下面是针对这个问题的实操方案,按步骤来就能解决:
1. 用独立线程分别读取输出流
绝对不要在主线程里直接读取Process的输出流,因为主线程很容易被其他逻辑阻塞,导致缓冲区堆积。正确的做法是为getInputStream()和getErrorStream()各开一个单独的线程,专门负责持续消费输出。
2. 推荐用ProcessBuilder代替Runtime.exec()
ProcessBuilder比传统的Runtime.getRuntime().exec()更灵活,比如可以合并标准输出和错误输出,减少线程数量,也能避免因为错误输出没读取导致的阻塞。
完整代码示例
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class TsharkCapture { public static void main(String[] args) { // 替换成你的实际tshark命令,这里以捕获两个IP的数据包大小为例 ProcessBuilder pb = new ProcessBuilder( "tshark", "-f", "host 192.168.1.100 and host 192.168.1.200", "-T", "fields", "-e", "frame.len" ); // 可选:合并标准输出和错误输出,这样只需要处理一个流,减少线程开销 pb.redirectErrorStream(true); try { Process process = pb.start(); // 启动独立线程读取输出流 Thread outputReaderThread = new Thread(() -> readStream(process.getInputStream())); outputReaderThread.start(); // 等待子进程结束(如果需要持续捕获,这部分可以去掉,改成监听进程状态) int exitCode = process.waitFor(); System.out.println("Tshark 进程退出,状态码:" + exitCode); // 等待读取线程完成剩余输出的读取 outputReaderThread.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } private static void readStream(InputStream inputStream) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; // 持续读取每一行,直到流关闭 while ((line = reader.readLine()) != null) { // 这里处理输出,比如打印到终端 System.out.println(line); // 如果需要做复杂业务处理,建议把行数据放到并发队列,用另一个线程处理 // 避免读取线程被阻塞,导致缓冲区再次堆积 } } catch (IOException e) { e.printStackTrace(); } } }
额外优化建议
- 强制tshark行缓冲:有些命令行工具在输出到管道时会用块缓冲(攒够一定数据才输出),可以给tshark加上
-l参数强制行缓冲,让它每输出一行就刷新缓冲区:tshark -l -f "host 192.168.1.100 and host 192.168.1.200" -T fields -e frame.len - 分离读取与处理逻辑:如果打印后需要做耗时的业务操作,别在读取线程里同步处理,用
ConcurrentLinkedQueue把行数据存起来,再开一个线程专门处理,让读取线程专注于快速消费流。
这样调整后,就能让tshark的输出持续流畅地打印到Java终端,不会再出现停顿了。
内容的提问来源于stack exchange,提问作者Vincent Durand
相关产品推荐
相关产品推荐

