I will be waiting for your knock ever after from today.


Visards, Inc.

Java Sticky Note

java
  ・JSTL
  ・Commons Net
  ・Apache
  ・Tomcat
  ・James
  ・Jetspeed
  ・POI
  ・Jexl
  ・Jelly
  ・Blojsom
  ・eclipse

link
  ・James

author
  ・profile

Commons  
<< prev | Index | next >>
Jakarta Commonsによるネットワークプログラミング

mixiチェック

5. Telnet
次に,Commons Netを使用して,Telnetクライア ントを作成します.
Telnetは,仮想端末を実現するために開発されたプ ロトコルです.そのため,端末機能を実現するための さまざまな通信規約が定められています(RFC854 他).Telnetプロトコルは,ASCIIベースのリクエスト コマンドとレスポンスからなる双方向の通信を基本構 造としており,これは単純で汎用性の高い構造です. Telnetのクライアントプログラムであるtelnetコマン ドが,しばしばHTTP,SMTP,POP3などほかのプ ロトコルの動作テストに使用されるのは,この汎用性 の高い特性を利用したものと言えます.前述の通り, Commons NetのFTPクラスがTelnetクラスを継承し て作成されているのも同様の理由です.
Telnetの基本部分は単純で汎用性の高い特性を持つ 一方で,Telnetプロトコル全体はそれほど単純な構造 ではありません.仮想端末を実現するために,端末情 報をやりとりするための手順や,表示に関する情報を やりとりするためのコマンドなど,多数のオプション 機能を有するためプロトコル全体としては複雑になっ ています.
Commons Netが提供するTelnetのクライアント機 能は,基本的な部分だけで,高度な機能はありませ ん.Socketベースのプログラミング経験があれば,後 述するサンプルプログラムが,Socketベースのプログ ラミングの流れと大差ないと感じられるでしょう.実 際には,Commons Netは,機種間の改行コードの差 を吸収したり,ネットワークからのデータの受信処理 をスレッドにより実行するなどの処理を行っています. 次節では,サンプルプログラムを基にCommons Net を用いたTelnetの処理の流れを説明します.
では,Telnetのサンプルクライアントを作成します.

5.1 Telnet サンプルプログラムの動作
今回のサンプルプログラムでは,仮想端末を実現す るような複雑な処理は行いません.Telnetサーバと接 続し,サーバのシェル上でコマンドを1つ実行し,そ の出力結果を取得してログアウトするプログラムを作 成します.
このサンプルプログラムと同様の処理をコマンドラ インから実行する場合の例を図3に示します.一般的 なUNIX上でtelnetを実行した場合,次のような処理 の流れとなります.

(1) ユーザは,telnetコマンドを用いてTelnetサーバ に接続を要求する
(2) telnetd(Telnetサーバ)は接続要求を受け付ける と,仮想端末デバイスを割り当て,ユーザ認証を 行うためにloginプロセスと呼ばれるプロセスを 起動する
(3) loginプロセスはログインとパスワードを要求し, ユーザ認証を行う
(4) loginプロセスによるユーザ認証が成功したら,ユ ーザ環境が初期化されシェルが起動される
(5) ユーザがシェルのプロンプト上でコマンドを入力 し,プログラムを実行する(ここでは,psコマン ドを実行しています)
(6) 必要な処理を実行したら,ユーザはログアウトし, Telnetを終了する

今回は,以上の処理を行うJavaプログラムを作成 します.この場合,Telnetサーバ上でのシェルの操作 が処理の大部分であり,Telnetプロトコルに依存した 処理は実際のところあまりありません.しかし,スト リームを使用した基本的な処理の流れは理解していた だけるのではないかと思います.

    図3 Telnet コマンドの実行例
    $ telnet localhost
     :
    SunOS 5.9
    login: <userid>
    Password: >password>
    Last login: Sat Oct 4 14:51:01 from localhost
    Sun Microsystems Inc. SunOS 5.9 Generic May 2002
    hostname 1> ps -aef
    UID PID PPID C STIME TTY TIME CMD
    root 0 0 0 9月19 ? 0:03 sched
    root 1 0 0 9月19 ? 0:03 /etc/init -
    …
    hostname 2> exit
    Connection closed by foreign host.
    

なお,本サンプルプログラムは,非常に簡略 化しています.ログインあるいはシェルの操作 は,システムに依存する場合がありますが,今 回はその点を考慮していませんので,必要に応 じて変更を加えてください.本サンプルプログ ラムでは例外処理などを実装していない点にも 注意してください.

5.2 プログラムの説明
リスト2がTelnetのサンプルプログラムです. FTP におけるFTPClient クラスのように, Telnetにおいて通信の中心的な役割を果たすク ラスがTelnetClient クラスです.ただし, TelnetClientは,接続と切断に関する処理を実 行する程度で,接続後のほとんどの処理は, TelnetClientクラスから取得できる入出力スト リームに対して行います.

リスト2
 1  import java.io.*;
 2  import java.util.regex.Pattern;
 3  import java.util.regex.Matcher;
 4  import org.apache.commons.net.telnet.TelnetClient;
 5  
 6  public class TelnetSample {
 7  
 8      // 特定のメッセージが出力されるまで、サーバからのメッセージを読み込む
 9      static String readMessage(Reader reader, String message)
10          throws Exception {
11  
12          Pattern pattern = Pattern.compile( message, Pattern.DOTALL );
13          StringBuffer buffer = new StringBuffer();
14          Matcher matcher = null;
15  
16          while( true ) {
17              int c = reader.read();
18              if (c < 0) break;
19              buffer.append((char)c);
20              if (reader.ready() == false) {
21                  matcher = pattern.matcher( buffer.toString() );
22                  if (matcher.matches()) break;
23              }
24          }
25  
26          if (matcher.find(0) && matcher.groupCount() >= 1) {
27              return( matcher.group(1) );
28          }
29          return( null );
30      }
31  
32  
33      public static void main(String[] arg) throws Exception {
34  
35          String user = "user";
36          String password = "password";
37          String prompt   = "hostname \\d+> $";
38  
39          // クライアントの生成
40          TelnetClient telnet = new TelnetClient();                ←(1)
41  
42          // サーバに接続
43          telnet.connect( "server_name" );                         ←(2)
44  
45          // 通信用の入出力ストリームの生成
46          InputStream istream = telnet.getInputStream();           ←(3)
47          OutputStream ostream = telnet.getOutputStream();         ←(3)
48          Reader reader = new InputStreamReader( istream );        ←(3)
49          Writer writer = new OutputStreamWriter( ostream );       ←(3)
50  
51          // 認証の実行
52          readMessage(reader, ".*login: $");                       ←(4)
53          writer.write( user + "\n" );                             ←(4)
54          writer.flush();                                          ←(4)
55          readMessage(reader, "Password: $");                      ←(4)
56          writer.write( password + "\n" );                         ←(4)
57          writer.flush();                                          ←(4)
58  
59          // プロンプト出力待ち
60          readMessage(reader, ".*" + prompt);                      ←(5)
61  
62          // コマンド実行
63          writer.write( "ps -aef\n");                              ←(5)
64          writer.flush();                                          ←(5)
65  
66          // 実行結果取得
67          String output = readMessage(reader, "(.*)" + prompt);    ←(6)
68  
69          // 実行結果の出力
70          System.out.print( output );                              
71  
72          // ネットワークの切断
73          telnet.disconnect();                                     ←(7)
74      }
75  }


リスト2の流れを以下に示します((1)がリス ト2 - (1)の部分に対応.以下同様).

(1) TelnetClientの生成
リスト2-(1)では,TelnetClientのオブジェク トを生成しています.

(2)サーバへの接続
サーバへの接続は,TelnetClientのconnect() メソッドを使用します.引数にサーバを指定し ます.接続に失敗した場合には,Exceptionが 発生します.

(3) 通信用のストリームの取得
TelnetClientクラスからデータの通信用スト リームを取得します.受信用のストリームは getInputStream()メソッドで,送信用のスト リームはgetOutputStream()メソッドで取得し ます.
このサンプルプログラムでは,Telnet上で送 受信されるデータがすべて文字列であることを前提と し,入出力のストリームから,それぞれのReader, Writerを生成しています.サーバマシンとクライアン トマシンの文字エンコーディングが異なる場合は,エ ンコーディングの変換処理が必要となります.

(4) 認証処理
loginプロセスが,ユーザ名の入力を求めるプロン プトを出力したらユーザ名を,パスワードの入力を求 めるプロンプトを出力したらパスワードを送信します. readMessage()メソッドは,最初の引数であるTelnet の入力ストリームから2番目の引数と合致する文字列 が得られるまでデータを読み込むメソッドです(2番 目の引数は,正規表現で記述するようになっていま す).つまり,リスト2 - 52行目では,サーバが,ログ イン名の入力プロンプトである「login:」という文 字列を出力するまでサーバからデータを読み込みます. そして,リスト2 - 55行目で,ユーザ名をサーバに送 信しています.パスワードについても同様の処理を行 っています.

(5) コマンドの起動
認証が成功すると,シェルプロンプトが出力される はずです.ここでは,シェルがプロンプトを出力され るのを待ち,シェルのプロンプトに対してコマンドを 与えることでプログラムを実行します(リスト2 - 63 行目).

(6)実行結果の取得
上記(5)で実行したコマンドが終了すると,再びシェ ルのプロンプトが出力されるはずです.readMessage() メソッドにより,次にシェルプロンプトが出力される まで待ちます.readMessage()メソッドは,この間に 出力された文字列を返り値として返します.つまりこ こでは,次のシェルプロンプトが出力されるまでのデ ータを,コマンドの実行結果として受け取っていま す.

(7)ネットワークの切断
TelnetClientのdisconnect()メソッドを使用して,接 続を切断します.入出力ストリームは,disconnect() を実行すると,クローズされます.

今回使用しているシステムでは,シェルのプロンプ トが「hostname 12>」のように,ホスト名,スペ ース,ヒストリ番号,>,スペースという構成です(図 3).今回のように,シェルプロンプトにヒストリ番号 を表示していたり,あるいはカレントディレクトリ名 を表示したりしている場合には,コマンドを実行する たびに出力されるプロンプトが異なります.今回は, リスト2 - 37行目で正規表現によりプロンプトの構造 を記述しました.これとは異なるプロンプトを使用し ている場合には,この部分の記述を変更してくださ い.
この例は,非常に簡単なTelnetクライアントのサン プルプログラムでした.Commons Netが提供する機 能は低レベルなものですが,同時にTelnetは,多くの プロトコルの基礎ともなるものです.





 << prev  ↑index  next >>


このドキュメントに関するご意見、ご要望などはまで。


Copyright (C) 2003-2005 Visards, Inc. All Rights Reserved.