モジュールを使ってみよう (1)

前へ << FTP クライアントを作ってみよう (5) モジュールを使ってみよう (2) >> 次へ

モジュールとは

perl にはモジュールという拡張機能があります。 モジュールには perl に標準で付いてくるモジュール (Socket モジュールもその一つです) と、 自分で後からインストールしなければならないモジュールがあります。

IO::Socket

ここではもう一つのモジュール、IO::Socket モジュールを紹介しましょう。 IO::Socket モジュールも標準モジュールなので、 わざわざインストールする必要はありません。

以下のスクリプトは、文字列を送って文字列を返すだけの echo クライアントです。

% ./echo-client-io-socket.pl hostname 1234
で、hostname のポート 1234 番に接続します。 もちろん hostname で echo サーバがポート 1234 を listen している 必要があります。
% ./echo-client-io-socket.pl
と引数を省略すると、localhost のポート7番に接続します。

接続すると、line 1、line 2…という文字列を echo サーバに送信し、 echo サーバからのレスポンスを受信します。

localhost:7 に接続します。
送信メッセージ: line 1
受信メッセージ: line 1
送信メッセージ: line 2
受信メッセージ: line 2
送信メッセージ: line 3
受信メッセージ: line 3
送信メッセージ: line 4
受信メッセージ: line 4
送信メッセージ: line 5
受信メッセージ: line 5
送ったものをそのまま返すだけの echo サーバですから、 もちろん送信メッセージと受信メッセージの内容は同じです。

echo-client-io-socket.pl

    1: #!/usr/local/bin/perl
    2: 
    3: # $Id: echo-client-io-socket.pl,v 1.1 2001/04/21 18:56:39 68user Exp $
    4: 
    5: use IO::Socket;                   # IO::Socket モジュールを使う。
    6: 
    7:                                   # ホスト名とポート番号を設定
    8: $host = shift || 'localhost';
    9: $port = shift || getservbyname('echo','tcp') || 7;
   10: 
   11: print "$host:$port に接続します。\n";
   12: 
   13: $socket = IO::Socket::INET->new(PeerAddr => $host,
   14:                                 PeerPort => $port,
   15:                                 Proto    => 'tcp',
   16:                                 );
   17: if ( ! $socket ){
   18:     die "接続できませんでした。 $!\n";
   19: }
   20: 
   21: for (1..5){
   22:                                   # 文字列を送信
   23:     print "送信メッセージ: line $_\n";
   24:     print $socket "line $_\n";
   25:     $socket->flush();
   26: 
   27:                                   # 文字列を受信
   28:     $buf = <$socket>;
   29:     print "受信メッセージ: $buf";
   30: }
   31: 
   32: $socket->close();
まず、
    5: use IO::Socket;                   # IO::Socket モジュールを使う。
で、IO::Socket モジュールを使うことを宣言します。 これを書かないとエラーになります。

これまでのクライアントで使った getservbyname、inet_aton、sockaddr_in、socket、connect は 一切出てきません。それらの部分は

   13: $socket = IO::Socket::INET->new(PeerAddr => $host,
   14:                                 PeerPort => $port,
   15:                                 Proto    => 'tcp',
   16:                                 );
によって全て内部で行われています。楽ですね。 引数には
  • PeerAddr … 接続したいホスト名
  • PeerPort … 接続したいポート番号
  • Proto … プロトコル名。ここでは 'tcp' を指定します
を渡します。
なお、Reuse=>1=> は , (カンマ) と同じ意味ですが、=> を使った方が、 引数の対応がわかりやすくなります。

また、$socket = IO::Socket::INET->new("$host:$port"); という省略形でも指定できます。

IO::Socket::INET->new がファイルハンドルを返すことに注意して下さい。 これを変数に代入し、これ以降はこの変数 (ファイルハンドル) に対して入出力を行います。


あとは、メッセージを送って受け取るだけです。

   21: for (1..5){
   22:                                   # 文字列を送信
   23:     print "送信メッセージ: line $_\n";
   24:     print $socket "line $_\n";
   25:     $socket->flush();
   26: 
   27:                                   # 文字列を受信
   28:     $buf = <$socket>;
   29:     print "受信メッセージ: $buf";
   30: }
文字列を送信した後は、flush でデータを送信しています。 毎回 flush でバッファをクリアせず、IO::Socket::INET->new の 後に
select($socket); $|=1;
と、バッファリングしない設定にするのでもいいでしょう。
perl5.005_05 の perldoc IO::Socket には
As of VERSION 1.18 all IO::Socket objects have autoflush turned on by default. This was not the case with earlier releases.
つまり、IO::Socket の version 1.18 以降は、IO::Socket の 生成するソケットは、デフォルトでバッファリングしない ようになっている、とあります。ここでは念のため、 明示的にバッファリングを OFF にしています。
文字列の送信後は、echo サーバから1行受け取り、それをそのまま表示します。
   32: $socket->close();
最後にソケットを close して終了です。

オブジェクト指向

オブジェクト指向とは何かを説明し出すと、いつになっても終わらないので、 表面的な部分だけ書きます。
use FOO;
$obj = FOO->new;
$obj->func1();
$obj->func2(10,20);
$obj->end();
use FOO で、クラス FOO を使用することを宣言します。 FOO->new とすると、クラス FOO のオブジェクトを返します。 オブジェクトとは、たくさんの変数が詰まったものだと思って下さい。 $obj->func1() とすると、クラス FOO のメソッド (関数のようなもの) である func1 を $obj に対して実行します。

C のような関数型プログラミングで書くと、

$data = FOO_init;
&FOO_func1($data);
&FOO_func2($data,10,20);
&FOO_end($data);
のような感じでしょうか。

はっきり言えば、変数名が先にくるか、それとも関数名が先にくるかの違いです。 「変数名->関数名」という書き方をオブジェクト指向というのだ、と 思ってもらって結構です。実際、

   32: $socket->close();
close($socket);
とも書けます。
こんな いいかげんな説明、OOP 好きの人が読んだら怒るだろうな…。
前へ << FTP クライアントを作ってみよう (5) モジュールを使ってみよう (2) >> 次へ

ご意見・ご指摘は Twitter: @68user までお願いします。