1

I have my application connecting to a server over SSH (Using JSch) but the app cant keep up if I send commands without waiting for the result to come back and it misses the reply as the next command gets sent without waiting.

I have tried a few methods e.g. using an Object to synchronize the Thread and using wait() and notify() but the UI thread stays frozen doing this. And thread.join() which worked but resulted in it running very slowly as it froze between commands.

Long story short, I need to wait for a reply from the server, process it and plot it, then send the next command.

Any help is greatly appreciated as this is for my Computing project. :D

    
//A snippet of my service procedure that handles the SSH connections

public String sendCommand(final String command) {
        Finished = false;
        final Thread sshServiceThread = new Thread(new Runnable() {
            String sshPassword = null;
            private volatile boolean running = true;

                @Override
                public void run () {
                    while (running) {
                        try {
                            session = jSch.getSession(username, host, port);
                            checkPassword();
                            session.setPassword(sshPassword);
                            session.setConfig("StrictHostKeyChecking", "no");
                            //TODO - REMOVE THIS
                            Log.w("DEBUG", "Host: " + host + " Username: " + username + " Password: " + password);
                            Log.w("Security", "REMOVE THIS BEFORE RELEASE");
                            session.connect();
                            channel = session.openChannel("shell");

                            DataOutputStream dataOut = new DataOutputStream(channel.getOutputStream());

                            InputStream in = channel.getInputStream();

                            channel.connect();

                            //Send command
                            Log.w("Command", "Command: " + command);
                            dataOut.writeBytes(command + " && echo TheCommandWasFinished" + "\r\n");
                            dataOut.flush();

                            byte[] tmp = new byte[1024];
                            String replyLine = "";
                            try {
                                while (true) {
                                    while (in.available() > 0) {
                                        int i = in.read(tmp, 0, 1024);
                                        if (i < 0)
                                            break;
                                        String lastLine = replyLine;
                                        replyLine = (new String(tmp, 0, i));

                                        if (!replyLine.contains("&& echo TheCommandWasFinished")) {
                                            if (replyLine.contains("TheCommandWasFinished")) {
                                                String result = lastLine;
                                                Log.w("Result", result);
                                                reply = result;
                                                synchronized (syncObject) {
                                                    notify();
                                                }
                                                return;
                                            }
                                        }
                                    }
                                }
                            } catch (Exception exception) {
                                Log.w("Exception", exception);
                            }

                        } catch (JSchException jschX) {
                            Log.w("Exception", jschX);

                            if (jschX.toString().contains("timeout")) {
                                Log.w("Error", "IP Address is incorrect");
                                Toast.makeText(getApplicationContext(), "Cannot connect to host, Check IP and connection.", Toast.LENGTH_LONG).show();

                            }

                            if (jschX.toString().contains("Auth fail")) {
                                Log.w("Error", "Username/Password Incorrect");
                                Toast.makeText(getApplicationContext(), "Username/Password Incorrect", Toast.LENGTH_LONG).show();
                            }

                            if (jschX.toString().contains("ECONNRESET")) {
                                Toast.makeText(getApplicationContext(), "Connection failure", Toast.LENGTH_LONG).show();
                            }

                        } catch (IOException Exception) {
                            Log.w("Exception", Exception);
                        }
                    }
                }
            void checkPassword() {
                Log.w("Password set", "Password: " + sshService.password);
                sshPassword = sshService.password;
            }
        });
        sshServiceThread.start();

        try {
            synchronized(syncObject) {
                syncObject.wait(3000);
            }
        }
        catch (Exception Exception) {
            Log.w("Exception", Exception);
        }
        Finished = true;
        return reply;
    }

// Last few lines of my Logcat

02-12 00:11:10.281  32701-32701/com.lonedev.slypanel W/Values﹕ Host: 192.168.0.14 Username: adam
02-12 00:11:10.282  32701-32701/com.lonedev.slypanel W/Save﹕ Saved 192.168.0.14 adam
02-12 00:11:10.354  32701-32701/com.lonedev.slypanel W/Host:﹕ 192.168.0.14
02-12 00:11:10.355  32701-32701/com.lonedev.slypanel W/Host:﹕ 192.168.0.14
02-12 00:11:13.398    32701-684/com.lonedev.slypanel W/Password set﹕ Password: **********
02-12 00:11:13.399    32701-684/com.lonedev.slypanel W/DEBUG﹕ Host: 192.168.0.14 Username: adam Password: **********
02-12 00:11:13.399    32701-684/com.lonedev.slypanel W/Security﹕ REMOVE THIS BEFORE RELEASE
02-12 00:11:13.794    32701-684/com.lonedev.slypanel W/Command﹕ Command: top -b -n2 | grep "Cpu(s)" | awk '{print $2 + $4}' | tail -1

 // This should return a value for cpu usage e.g. 37.5
// UI Freezes here

I have read that I could use an AsyncTask here instead of a thread... Not sure if I can do that inside of a Service though... :P

Adam Price
  • 15
  • 5

1 Answers1

0

See #sending-commands-to-server-via-jsch-shell-channel

... if you are not sure how many lines to read and thus want to use "while", make sure you do something inside while to prevent 1) busy-waiting 2) ending-condition. Example:

while(!end)
{
   consoleOutput.mark(32);
   if (consoleOutput.read()==0x03) end = true;//End of Text
   else
   { 
     consoleOutput.reset();
     consoleOutput.readLine();
     end = false;
   }
}

To process your in-out from the other Thread, you may use #non-blocking-buffer

Take a look over the jsch examples.

Also, as design suggestion, you may use UserInfo interface for passing your credentials, and not directly from your UI objects.

A similar project with yours (web based, not android): https://github.com/kohsuke/ajaxterm4j

Community
  • 1
  • 1
azbarcea
  • 3,323
  • 1
  • 20
  • 25