Perl で UNIX ドメインのサーバを書くことになりました。でもSocket(or IO::Socket)をそのまま使うのは面倒くさい*1。ということで CPAN をあさってたら Net::Server というものがありました。POE というのもあって最終目標からすると勉強する価値ありそうだったんですが,さすがに牛刀ぽかったので Net::Server で書いてみることにしました。
接続すると単純に /bin/date コマンドの内容を返す(そして close する)サーバを書いてみます。DateServer.pm という名前で下記のとおり。
package DateServer;
use strict;
use base qw( Net::Server::Fork );
use IPC::Run;
sub new {
my ($class) = @_;
return $class->SUPER::new({
proto => 'unix',
port => 'dateserv',
});
}
sub process_request {
my ($self) = @_;
warn 'accepted';
eval {
my ($in, $out, $err);
IPC::Run::run(
[qw( /bin/date )],
\$in, \$out, \$err,
IPC::Run::timeout(10)
)
or die $?;
print STDOUT $out, "\n";
};
croak $@ if $@;
warn 'bye bye';
}
1;コマンドの実行結果を取得するのには何を使おうとまた悩んだんですが,先のことを考えて IPC::Run というものを使ってみました。なので,上記はほとんど Net::Server と IPC::Run の EXAMPLES を融合しただけです。
IPC::Run::run のあたりは苦肉の策で,Net::Server にも run というコマンドがあるんでバッティングしてしまってうまく動かなかったため run を import せずに書いています。
Net::Server::Fork を使っているので接続の度に子プロセスを fork する形になっていますが,Apache のように prefork するものや inetd 用コマンドにするためのものもあります。
このモジュールを利用したサーバスクリプトは
#!/usr/bin/perl use strict; use DateServer; my $server = DateServer->new(); $server->run();
Net::Server のサンプルでは new するときにパラメータを与えていましたが,面倒だったのでモジュール側につっこんでおきました。本来はコマンドラインからポート番号(UNIX ドメインだからパス名か)を指定したりできるように,やはり new から指定できるようにするべきだと思います。
このスクリプトを実行すると,
2006/09/08-17:54:38 DateServer (type Net::Server::Fork) starting! pid(22660) Binding to UNIX socket file dateserv using SOCK_STREAM
このように画面に出力され,サーバプロセスが始動します。
あとはここに telnet でつなぎにいく…って UNIX ドメインだから telnet 使えないや*2。
#!/usr/bin/perl
use strict;
use IO::Socket::UNIX;
my $socket = IO::Socket::UNIX->new( Peer => 'dateserv' )
or die $!;
while (my $data = <$socket>) {
print $data;
}
$socket->close();これは逆に UNIX ドメインにつなぎにいって出力があればあるだけ画面に出すというスクリプトです(結局 IO::Socket を使ってしまった…)。
この client.pl を実行すると…
Fri Sep 8 18:00:26 JST 2006
改行が一行おおいですがうまくサーバからとってこれました。
逆にサーバプロセスのログ(というか画面出力)をみると,
accepted at DateServer.pm line 19. bye bye at DateServer.pm line 35.
仕込んでおいた warn がきちんと吐いていることがわかります。
うーん,楽ちん。