Saturday, December 31, 2011

Telnet in Java

  Le Trung Thang, end of 2011 

Telnet is a network protocol used on the Internet or local area networks to provide a bidirectional interactive text-oriented communications facility using a virtual terminal connection. User data is interspersed in-band with Telnet control information in an 8-bit byte oriented data connection over the Transmission Control Protocol (TCP) (soure: http://en.wikipedia.org/wiki/Telnet).
In Java, a good library for Telnet is Apache Commons Net (http://www.apache.org). As mentioned, Telnet is a bidirectional interactive protocol. So, when the client telnets to a host, it need wait for response from the host. This waiting time is significant that needs consider when you need read data from the host device. In the example following, we telnet to a host device which is a Impinj RFID reader, to login into the host machine. Whenever the host device receive a connection request from client, it will response to the client to request input the user and password with format of feedback message is "login: " and "Password: ".

package Test.Driver;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintStream;

import org.apache.commons.net.telnet.TelnetClient;

public class LLRPmonitor {

    private TelnetClient        telnet            = new TelnetClient();
    private BufferedInputStream    input;
    private PrintStream            output;           // output stream
    private final int waitTime = 500; //wait time response from reader, in ms
    
    // ===================================
    public LLRPmonitor() {
        super();
    }

    /**
     * Connect to reader
     * 
     * @param userName
     *            : The user name for login
     * @param password
     *            : The password for login
     *@param IPReader
     *            : IP address of reader
     * @return boolean. connect OK or Fail
     * @throws IOException
     *             Any problems during connect
     * @author ThangLe
     */
    private boolean connect(String IPReader, String userName, String password) {
        try {
            
            if (telnet != null && telnet.isConnected()) {
                telnet.disconnect();
            }
            if (input != null) {
                input.close();
            }
            if (output != null) {
                output.flush();
                output.close();
            }
            
            // Connect to the specified server
            telnet.setConnectTimeout(5000);// Timeout 5s
            telnet.connect(IPReader, 23);

            // Get input and output stream references            
            input = new BufferedInputStream(telnet.getInputStream());
            output = new PrintStream(telnet.getOutputStream());

            Thread.sleep(waitTime); // wait for responding from reader
            // Log the user on
            if (readUntil("login: ") == null) return false;
            write(userName);
            Thread.sleep(waitTime); // wait for responding from reader
            if (readUntil("Password: ") == null) return false;
            write(password);
            Thread.sleep(waitTime); // wait for responding from reader
            // Advance to a prompt
            // readUntil(prompt + " ");

            return true; // connect OK
        } catch (Exception e) {
            e.printStackTrace();
            return false; // connect fail
        }
    }

    

    // ================================
    /*
     * 
     */
    private String readUntil(String pattern) {
        try {
            char lastChar = pattern.charAt(pattern.length() - 1);
            StringBuffer sb = new StringBuffer();
            int numRead = 0;
            
            if(input.available()<= 5){//reader always returns more 5 chars
                return null;
            }
            char ch = (char) input.read();

            while (true) {
                // System.out.print(ch);
                numRead++;
                sb.append(ch);
                if (ch == lastChar) {
                    if (sb.toString().endsWith(pattern)) {                        
                        return sb.toString();
                    }
                }
                
                if(input.available()==0){
                    break;
                }
                ch = (char) input.read();

                if (numRead > 2000) {
                    break; // can not read the pattern
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // =================================
    private void write(String value) {
        try {
            output.println(value);
            output.flush();
            // System.out.println(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    
    // ==================================
    private void disconnect() {
        try {            
            telnet.disconnect();            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // ===================to Test==================

    public static void main(String[] args) {
        try {
            LLRPmonitor llrpMonitor = new LLRPmonitor();
            llrpMonitor.connect("192.168.10.100","root","pass");
            llrpMonitor.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
In above code snippet, what happens when remove all lines: Thread.sleep(waitTime)?
About logic, this code is correct. You opened a connection to device, send some commands to the host device and getting back the results from the host.  However, when you run the program with a real device, maybe you are never able to log in to the host device. Why?
As you saw, Telnet is a bidirectional interactive protocol. So, when you send a command to host, there need a certain period of time to the host can send response back to the client. Depending of network speed, it can spend about a few hundred of milliseconds to the client can receive response message from the host. When you remove the line: Thread.sleep(waitTime), Program will be not waiting for this period of time, so you cannot receive response message which can be sent back later. Of course, you can replace Thread.sleep by the other ways.
The source code below is complete code to get LLRP status of Impinj RFID reader (www.impinj.com) by using Telnet command. The result receiving will be either ROSpec ACTIVE, INACTIVE or no existing ROSpec in reader.

package Test.Driver;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.apache.commons.net.telnet.TelnetClient;

public class LLRPmonitor {



    private TelnetClient        telnet    = new TelnetClient();
    private BufferedInputStream    input;
    private PrintStream            output;    // output stream

    // ========Tokens=============
    private final String        promptMessRev    = "LLRPAccessSpecCount";
    private final String        promptNumROS    = "LLRPROSpecCount='";
    private final String        promptStatusROS    = "LLRPROSpec1='";
    private final int waitTime = 500; //wait time response from reader, in ms

    // ===================================

    public LLRPmonitor() {

        super();

    }

    /**

     * Connect to reader
     * 
     * @param userName
     *            : The user name for login
     * @param password
     *            : The password for login\
     *@param IPReader
     *            : IP address of reader
     * @return boolean. connect OK or Fail
     * @throws IOException
     *             Any problems during connect
     * @author ThangLe
     */

    private boolean connect(String IPReader, String userName, String password) {

        try {
            
            if (telnet != null && telnet.isConnected()) {
                telnet.disconnect();
            }

            if (input != null) {
                input.close();
            }

            if (output != null) {
                output.flush();
                output.close();
            }
            
            // Connect to the specified server
            telnet.setConnectTimeout(5000);// Timeout 5s
            telnet.connect(IPReader, 23);

            // Get input and output stream references            
            input = new BufferedInputStream(telnet.getInputStream());
            output = new PrintStream(telnet.getOutputStream());

            Thread.sleep(waitTime); // wait for responding from reader
            // Log the user on
            if (readUntil("login: ") == null) return false;
            write(userName);
            Thread.sleep(waitTime); // wait for responding from reader
            if (readUntil("Password: ") == null) return false;
            write(password);
            Thread.sleep(waitTime); // wait for responding from reader
            // Advance to a prompt
            // readUntil(prompt + " ");

            return true; // connect OK
        } catch (Exception e) {
            e.printStackTrace();
            return false; // connect fail
        }
    }

    /**
     * Check LLRP status
     * 
     *@param IPReader
     *            : IP address of reader
     * @return int: 
     *         0: Has no the ROSPEC in reader.
     *         1: The ROSPEC is activating.
     *         2: The ROSPEC is inactive.
     *         -1: A error occurred
     * @throws Exception
     *             Any problems during connect
     * @author ThangLe
     */

    public synchronized int checkROSPEC(String IPReader) {

        String ROstatus = "";
        int index = 0;

        try {
            if (!connect(IPReader, "root", "impinj")) {//connect and login to reader
                return -1; // connect fail
            }

            // ==================
            ROstatus = sendCommand("show rfid llrp summary");
            disconnect(); // stop connect to reader

            if (ROstatus == null) return -1;

            // Start processing received data from reader
            // ===============get number of the ROSPEC in reader====================

            index = ROstatus.indexOf(promptNumROS);
            if (index != -1) {
                String NumROS = ROstatus.substring(index + 17, index + 18);
                // System.out.println(NumROS);
                if (NumROS.equals("0"))
                    return 0; // has no ROSPEC in reader
            }

            // ==========get ROSPEC status=========
            index = ROstatus.indexOf(promptStatusROS);
            if (index != -1) {
                String statusROS = ROstatus.substring(index + 13, index + 19);
                // System.out.println(statusROS);
                if (statusROS.equals("Active"))
                    return 1; // ROSPEC is Activated
                else
                    return 2;// ROSPEC is Inactivated
            }
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
        return -1;
    }

    // ================================
    /*
     * 
     */

    private String readUntil(String pattern) {

        try {
            char lastChar = pattern.charAt(pattern.length() - 1);
            StringBuffer sb = new StringBuffer();
            int numRead = 0;
        
            if(input.available()<= 5){//reader always returns more 5 chars
                return null;
            }
            char ch = (char) input.read();

            while (true) {
                // System.out.print(ch);
                numRead++;
                sb.append(ch);
                if (ch == lastChar) {
                    if (sb.toString().endsWith(pattern)) {                        
                        return sb.toString();
                    }
                }
                
                if(input.available()==0){
                    break;
                }
                ch = (char) input.read();
                if (numRead > 2000) {
                    break; // can not read the pattern
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // =================================

    private void write(String value) {
        try {
            output.println(value);
            output.flush();
            // System.out.println(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // ===================================

    private String sendCommand(String command) {
        try {
            write(command);
            Thread.sleep(waitTime); // wait for responding from reader
            return readUntil(promptMessRev);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // ==================================
    private void disconnect() {
        try {            
            telnet.disconnect();            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // ===================to Test==================
    public static void main(String[] args) {
        try {
            LLRPmonitor llrpMonitor = new LLRPmonitor();
            System.out.println(llrpMonitor.checkROSPEC("192.168.10.250"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Impinj Reader
 Download example Source Code

 LLRP-ReaderInfo: checks RFID reader status (for LLRP protocol)

 

Download program

8 comments:

  1. nice guide !
    walk around on the internet, I found some guys already developed a few nice plug-in for eclipse:

    + with remote systems, we can explore the files on the server ( this plug-in use ftp, telnet, sctp to fetch files ). The image show the result of plug-in that I'm using:
    http://i486.photobucket.com/albums/rr223/mathhoang/fpt_eclipse_mathhoang.png

    + to ssh into one server directly, there're a few plug-in to do that, but terminal plug-in is the good one I'm using:
    http://i486.photobucket.com/albums/rr223/mathhoang/ssh_eclipse_mathhoang.png

    ReplyDelete
  2. Thanks Hoang,

    We use Eclipse plug-in in case of handle testing. If you want integrate it to your program, i think you should use libraries instead of plug-in. Right ?

    ReplyDelete
  3. //catch the inputstream from device in a loop.
    TelnetClient tc;
    InputStream instr;
    byte[] buff = new byte[60000000];
    int ret_read = 0;

    do {
    instr = tc.getInputStream();
    ret_read = instr.read(buff);
    }while (ret_read ==0);

    ReplyDelete
  4. //catch the inputstream from device in a loop.
    TelnetClient tc;
    InputStream instr;
    byte[] buff = new byte[60000000];
    int ret_read = 0;

    do {
    instr = tc.getInputStream();
    ret_read = instr.read(buff);
    Thread.sleep(1000);
    }while (ret_read ==0);

    ReplyDelete
  5. Thank you very much it helped a lot. Even in 2015 we still need the same code!

    ReplyDelete
  6. It is power of Java. Write once, use forever :))

    ReplyDelete
  7. Thank you! This will help me immensely.

    ReplyDelete