【问题标题】:JSch ChannelExec OutputStream not showing the program output. However it works with shell scriptJSch ChannelExec OutputStream 不显示程序输出。但是它适用于 shell 脚本
【发布时间】:2018-06-19 23:24:57
【问题描述】:

我在本地机器上有一个用 java 编写的程序。此程序使用 JSch 连接到远程计算机,并在用户界面中的 MessageConsole 上显示输出。

我可以在 UI 中更新MessageConsole,即使我启动的脚本没有终止并输出随机数。我可以阅读OutputStream 并将其显示在MessageConsole 上。它工作得很好。

我的问题是启动 C 程序后的输出。 OutputStream 上没有显示任何内容。 仅当程序完全终止时才会出现输出。 所有输出都使用

printf

std::cout

如果我手动登录远程机器并手动启动程序,我的终端会立即显示所有输出。

我使用ChannelExec 在远程机器上启动程序:

String program = "sudo ./foo/bar.out";
String continousOutput = "sh ~/foo/bar.sh";
//ProcessWorker(String command, SSHSession ssh, GUI gui)
pw = new ProcessWorker(program, ssh, this);
pw.execute();

ProcessWorker pw 是一个 SwingWorker 类,用于在后台保持连接打开并更新 GUI 而不会锁定它。

这段代码完成了所有的脏活。 SSH 连接已经建立并且可以正常工作。

protected Void doInBackground() throws Exception {
    try {
        exec = ssh.session.openChannel("exec");
        //command  is the String  program or continousOutput
        ((ChannelExec)exec).setCommand(command);

        exec.setInputStream(null);

        ((ChannelExec)exec).setErrStream(System.err);
        InputStream in = exec.getInputStream();
        exec.connect();

        byte [] tmp = new byte[1024];
        while(true) {
            while(in.available()>0) {
                int i = in.read(tmp, 0, 1024);
                if(i<0)break;
                String response = new String(tmp, 0, i);
                check(response); //parse response
                System.out.println(response);
                //this.publish(); //desperate try to get something
            }
            if(exec.isClosed()) {
                if(in.available() > 0) continue;
                System.out.println("exit-status: " + exec.getExitStatus());
                break;
            }
            try {Thread.sleep(1000);} catch(Exception ee) {}
        }

        exec.disconnect();
    } catch (JSchException | IOException e) {
        e.printStackTrace();
    }

    return null;
}    

我不知道问题出在哪里。我的意思是它与脚本完美配合。

有什么想法吗?

【问题讨论】:

    标签: java linux shell ssh jsch


    【解决方案1】:

    简短的回答是为远程会话分配 PTY(伪 TTY)。这可能会导致远程进程在运行时发出其输出,而不是在最后发出所有输出:

    exec.setPty(true);
    

    当您通过 unix 命令行运行程序时,您是通过 TTY 设备与程序通信。当您通过 SSH 为会话请求 PTY (TTY) 启动程序时,您正在通过一组管道与程序通信,每个管道用于标准输入、输出和错误。

    Unix 程序通常会缓冲它们的输出。缓冲行为内置于大多数 C 程序使用的标准 I/O 逻辑中(也被 perl、python 等使用或复制)。写入 TTY 时,典型行为是缓冲输出数据,直到写入整行(即程序写入换行符),然后将整行立即写入 TTY。这就是您在交互式运行程序时所体验到的。

    当写入管道或文件时,默认行为是缓冲直到缓冲区被填满。缓冲区的大小可能为 8 KB。这种行为是基于这样的假设,即输出不会发送给人类,因此程序应该以最有效的方式运行。

    您的描述听起来像是程序正在对其输出进行块缓冲,并且在程序完成之前它没有产生足够的输出来刷新缓冲区。您希望程序改为对其输出进行行缓冲(或完全取消缓冲)。有几种方法可以做到这一点:

    1. 您说程序在交互式运行时可以正常工作。正如我所指出的,为 ssh 会话请求 PTY 可能会产生这种行为。
    2. 您可以查看系统上是否有类似stdbufunbuffer 的程序可用。这些可用于更改进程的缓冲行为。
    3. 如果您有相关程序的源代码,您可以在程序执行时的特定时间向alter the default buffering behaviorflush the buffer 添加逻辑。

    【讨论】:

    • 虽然使用 PTY 会给您带来讨厌的副作用。如果缓冲被证明是罪魁祸首,OP 最好将 C 程序改为不缓冲。
    • 感谢您提供的所有信息,这非常好。但似乎我无法使用以下方法终止进程: exec.sendSignal("2");为什么它不起作用?在终端中按 ctrl +c 可以正常工作。
    • 远程系统可能正在运行 OpenSSH ssh 服务器。我可能是错的,但是I don't think that server implements sending signals. 按下 ctrl-C 可能会杀死您的本地进程,导致远程进程在TTY closes. 时获得 SIGHUP
    • 会不会是正在运行的程序是使用sudo执行的,而发送的信号不是?我无法使用 kill pid 停止程序 - 但我可以使用 sudo kill pid 停止程序。有没有办法将 ChannelExec “升级”为 su?
    • 现在没关系。我刚刚在一个新频道中使用“sudo kill $(ps -U root | grep 'a.out' | awk '{print $1}')” 做了一个解决方法,就像这样终止第一个程序。感谢您的所有帮助。
    猜你喜欢
    • 1970-01-01
    • 2015-10-18
    • 2015-02-24
    • 1970-01-01
    • 1970-01-01
    • 2015-02-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    相关资源
    最近更新 更多