SSHTools Knowledgebase
Information and FAQs about SSHTools products
  
Search  
   
Browse by Category
SSHTools Knowledgebase .: J2SSH Maverick .: Tutorials .: Using channel events to receive data

Using channel events to receive data

The Maverick SSH API was originally designed as a single threaded API; this meant that all read and writes on a channelcould potentially block until data arrived. With the most recent version of J2SSH Maverick an optional buffered mode was introduced which provides a background thread to read and buffer channel data.

This has provided a couple of benefits to developers using the API. First of all, a channel InputStream's available method will now return a true representation of the number of unread bytes in the local buffer. Previously this method would always return zero until a blocking read had been performed because the SSH protocol multiplexes several channels into a single connection. This means there is no reliable way to determine how much data is destined for a channel before performing a potentially blocking read on the underlying Socket.The thread enables the connection to deliver data into the channel's local buffer asynchronously of any read operations performed by callers of the API.

To establish a buffered connection use the following call to SshConnector:

SshConnector con = SshConnector.getInstance();



SshClient ssh = con.connect(new SshTransport("", 22), "lee", true); // This final parameter tells the
// connection to use buffered mode

The second benefit that a bufferedmode provides is to enable events to be fired when data arrives in the local buffer. The ChannelEventListener interface has been around since the original conception of the API but the events were only fired when the data was read from the InputStream. This is still the behaviour for a single threaded connection, but when the buffered mode is selected, the events are fired by the background thread as soon as the data arrives.

To demonstrate I will create a simple example that starts a shell, reads input from the user through System.in and receives all the output of the remote process using events fired by the background thread.

First of all lets continue and authenticate the user:

// Create a password authentication object
PasswordAuthentication pwd = new PasswordAuthentication();

// Replace our xxxxx with the users password
pwd.setPassword("xxxxx");
if(ssh.authenticate(pwd)) {

}

Now we have an authenticated connection, so lets create a ChannelEventListener implementationthat outputs all the data recieved to System.out:

ChannelAdapter eventListener = new ChannelAdapter() {
public void dataReceived(SshChannel channel,
byte[] buf,
int offset,
int len) {
System.out.write(buf, offset, len);
}
 public void channelClosed(SshChannel channel) {

// Force our System.in reader thread to exit when the
// user presses a key
System.out.println("Press any key to exit.");
}
}; 

In the above code we have used the utility class ChannelAdapter to create our ChannelEventListener implementation and overriden two methods; the first method dataReceived will be called whenever process output arrives on the channel, which in our case we have simply written to System.out. The second method channelClosed will be called when the channel closes; as you will soon learnwe need to request that the user press a key so that the main thread will break out of blocking read on System.in and re-evaluate the state of the session.

Now that we have setup the listener we can open up a session and start the users shell. Since we are reading input from the user on System.in we can turn off the echo on the session as we already have user input in our local terminal.

// Create a session channel passing in our listener
SshSession session = ssh.openSessionChannel(eventListener);
// Use the newly added PseudoTerminalModes class to
// turn off echo on the remote shell
PseudoTerminalModes pty = new PseudoTerminalModes(ssh);
pty.setTerminalMode(PseudoTerminalModes.ECHO, false);


session.requestPseudoTerminal("vt100", 80, 24, 0, 0, pty);

Before we start the users shell we also need to configure the channel to consume all of the incoming data as it arrives. This is required because SSH channels have a window space mechanism which limits the amount of data that can be sent by the remote side.Window space only becomes available when datais read from the InputStream andso within a normal channel configuration we wouldneed to constantly be readingdata from the stream to enable the remote side to send further data. Setting the channel toautomaticallyconsume incoming dataremoves the need to readdata from the InputStream as it arrives.

// Tell the channel to consume all data as it arrives
session.setAutoConsumeInput(true);

// Start the user's shell session.startShell();

// Read data from System.in untill the session is closed
int read; while ((read = System.in.read()) > -1 && !session.isClosed()) { session.getOutputStream().write(read); }
ssh.disconnect();

You should now be able to compile your code and run the example. As you will see the users shell will be started and youcan send commands and receive output. When you want to quit simply type"exit"and return to close the session and then press any key to force the last remaining blocking readto return.

This concludes our events tutorial, you should now be able to create ChannelEventListener's and add them to your session's to listen to all the available channel events.


How helpful was this article to you?

User Comments

Add Comment
No comments have been posted.


powered by Lore
© 2008 SSHTools Ltd. All Rights Reserved