2014年5月2日 星期五

Java Telnet Client 類別

今天寫完周報後, 花了一整天把 Telnet Client 程式物件化, 到下班前終於執行成功, 搞定. 我不把它寫進 JT 類別, 因為那很彆扭, 而且也不是一般常用的工具, 應寫成獨立類別為宜, 為了簡單起見, 我將其命名為 Telnet.java, 改寫自上一篇文章 :

# Java 的 Telnet 實作 (續)

Telnet.java

import java.io.*;
import java.net.*;
public class Telnet {
  Socket socket;
  BufferedInputStream in;
  PrintWriter out;
  public boolean connect(String host) {
    return connect(host,23);
    }
  public boolean connect(String host,int port) {
    try {
      socket=new Socket(host, port);
      socket.setKeepAlive(true);
      in=new BufferedInputStream(socket.getInputStream());
      out=new PrintWriter(socket.getOutputStream(),true);
      return true;
      }
    catch (Exception e) {e.printStackTrace();return false;}
    }
  public boolean login(String usr, String pwd) {
    try {
      String res=readUntil("Enter User Name");
      if (res!=null) {
        res=readUntil(">");
        if (res != null) {
          send(usr);
          Thread.sleep(500);
          res=readUntil("Enter Password");
          if (res != null) {
            send(pwd, false);
            Thread.sleep(500);
            res=readUntil(">");
            if (res != null) {return true;}
            }
          }
        }
      return false;
      }
    catch(Exception e) {e.printStackTrace();return false;}
    }
  public void send(String command) {
    send(command, true);
    }
  public void send(String command, boolean echo) {
    try {
      out.println(command + "\n");
      out.flush();
      if (echo) {System.out.println(command);}      
      }
    catch (Exception e) {e.printStackTrace();}
    }
  public String readUntil(String pattern) {
    try {
      char lastChar=pattern.charAt(pattern.length()-1);
      StringBuffer sb=new StringBuffer();
      char ch=(char)in.read();    
      while(true) {
        System.out.print(ch);
        sb.append(ch);
        if (ch==lastChar && sb.toString().endsWith(pattern)) {
          return sb.toString();
          }  
        ch=(char)in.read();
        }
      }
    catch(Exception e) {e.printStackTrace();}
    return null;
    }
  public void disconnect() {
    try {in.close();out.close();socket.close();}
    catch(Exception e) {e.printStackTrace();}
    }
  }

此 Telnet 類別不是可以泛用的類別, 因為其中的 login() 方法乃是針對公司某主機所特製, 如果是另外一台 Telnet 伺服主機, 其登入情況不同, 必須另行撰寫, 這樣好像失去寫進類別的意義咧. 但先求有再求好, 以後再來看看是否能一般化吧.

編譯後放在同一目錄下, 接著就可以使用它來連線了, 測試程式如下 :

public class test12 {
  public static void main(String[] args) {
    Telnet client=new Telnet();
    if (client.connect("aa.bbb.cc.dd",23)) {  //連線主機
      String usr="asabulu";
      String pwd="blabla";  
      if (client.login(usr,pwd)) { //登入主機
        client.send("lis table users all;");  //傳送指令
        String res=client.readUntil("BOTTOM");  //取得主機回應資料
        }
      else {System.out.println("login failed!");}
      }
    else {System.out.println("connect failed!");}
    client.disconnect();  
    }
  }

執行後成功列印出所需資料. 下一步要改成將主機回應資料儲存至指定檔案. 其次 login() 方法應該自 Telnet.java 中移出到應用程式中來, 因為它會因伺服器不同而需改寫. (2014-05-02)

2014-05-05 補充 :

今天改寫上周完成的 Telnet.java 類別如下 :

import java.io.*;
import java.net.*;
public class Telnet {
  Socket socket;
  BufferedInputStream in;
  PrintWriter out;
  public boolean connect(String host) {
    return connect(host,23);
    }
  public boolean connect(String host,int port) {
    try {
      socket=new Socket(host, port);
      socket.setKeepAlive(true);
      in=new BufferedInputStream(socket.getInputStream());
      out=new PrintWriter(socket.getOutputStream(),true);
      return true;
      }
    catch (Exception e) {e.printStackTrace();return false;}
    }
  public void send(String command) {
    send(command, true);
    }
  public void send(String command, boolean echo) {
    try {
      out.println(command + "\n");
      out.flush();
      if (echo) {System.out.println(command);}      
      }
    catch (Exception e) {e.printStackTrace();}
    }
  public String readUntil(String pattern) {
    return readUntil(pattern,100000);
    }
  public String readUntil(String pattern,int maxLoops) {
    try {
      char lastChar=pattern.charAt(pattern.length()-1);
      StringBuffer sb=new StringBuffer();
      char ch=(char)in.read();
      int cnt=0;
      while(true) {
        System.out.print(ch);
        sb.append(ch);
        if (ch==lastChar && sb.toString().endsWith(pattern)) {
          return sb.toString();
          }  
        ch=(char)in.read();
        if (++cnt > maxLoops) {break;}
        }
      }
    catch(Exception e) {e.printStackTrace();}
    return null;
    }
  public void wait(int ms) {
    try {Thread.sleep(ms);}
    catch(Exception e) {e.printStackTrace();}
    }  
  public void disconnect() {
    try {in.close();out.close();socket.close();}
    catch(Exception e) {e.printStackTrace();}
    }
  }

主要是加入 wait() 方法, readUntil() 加入無限迴圈控制, 以及將 login 移出. 應用程式如下 :

public class fetchA {
  Telnet client;
  public static void main(String[] args) {
    new fetchA();
    }
  public fetchA() {
    StringBuffer sb=new StringBuffer();  //儲存擷取之資料
    client=new Telnet();
    if (client.connect("aa.bbb.cc.dd",23)) {
      String usr="asabulu";
      String pwd="blabla";
      String res=login(usr,pwd);
      if (res != null) {
        sb.append(res);
        client.send("LIST TABLE USERS ALL;");
        res=client.readUntil("BOTTOM");
        if (res != null) {
          sb.append(res);
          res=logout();
          sb.append(res);
          }
        }
      else {System.out.println("login failed!");}
      }
    else {System.out.println("connect failed!");}
    JT.writeFile("test.log",sb.toString());  //擷取之資料寫入檔案
    client.disconnect();
    }
  public String login(String usr, String pwd) {
    String res=client.readUntil("Enter User Name");
    if (res!=null) {
      res=client.readUntil(">");
      if (res != null) {
        client.send(usr);
        client.wait(500);
        res=client.readUntil("Enter Password");
        if (res != null) {
          client.send(pwd, false);
          client.wait(500);
          res=client.readUntil(">");
          if (res != null) {return res;}
          }
        }
      }
    return null;
    }
  public String logout() {
    /*
    >LOGOUT
    BYE BYE
    Asabulu Logged out on 2014/05/05 at 11:18:29.
    */
    StringBuffer sb=new StringBuffer();
    String res=client.readUntil(">");
    sb.append(res);
    if (res!=null) {
      client.send("LOGOUT;");
      res=client.readUntil(".");
      if (res != null) {
        sb.append(res);
        return sb.toString();
        }
      }
    return null;
    }
  }

這個 fetchA 程式是專為公司的 A 主機而寫, 每一台主機的登入方式都不一樣, 所以必須移出 Telnet 類別加以客製化, 分別撰寫 login() 與 logout() 方法. 此處 A 主機 LOGOUT 後會輸出登出時間, 為了保留完整資訊, readUntil() 讀取到日期後面的小數點時, 表示已經輸出登出資訊了.


沒有留言 :