Created
May 24, 2012 16:01
-
-
Save keiya/2782414 to your computer and use it in GitHub Desktop.
Perl Multithread & Multiprocess Socket Program
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
サーバ:マルチプロセスモデルのEchoサーバ、1プロセス1クライアント。 | |
クライアント:マルチスレッド(threads)のクライアント | |
(標準入力に1スレッド,受信(と標準出力)に1スレッド,送信に1スレッドで、情報はキューで受け渡し) | |
クライアントの方はなぜかシグナルハンドラがゴミでシグナルが無視される。 | |
いちばん手間かかったのがtty周りという。stty rawとかしてる。cursesとかつかえば簡単なんだろうけど。 | |
でもこれでssh/telnetもどきができる土俵は整ったかな。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# raw stdin echo client | |
# keiya_21@yahoo.co.jp | |
# thanks to | |
# main socket program http://d.hatena.ne.jp/perlcodesample/20100512/1276960096 | |
# thread program http://d.hatena.ne.jp/ashitano244/20090129/1233208853 | |
use strict; | |
use warnings; | |
use Socket; | |
use threads; | |
use Thread::Queue; | |
$| = 1; | |
our $sock; | |
&connect($sock); | |
$SIG{'INT'} = 'disconnect'; | |
$SIG{'TERM'} = 'disconnect'; | |
$SIG{'QUIT'} = 'disconnect'; | |
my $queue = new Thread::Queue; | |
my $write_thread = threads->new(\&tx); | |
my $read_thread = threads->new(\&rx); | |
my $stdio_thread = threads->new(\&stdio); | |
$write_thread->join; | |
$read_thread->join; | |
$stdio_thread->join; | |
sub connect { | |
# 1. ソケットの作成 | |
socket( $main::sock, PF_INET, SOCK_STREAM, getprotobyname('tcp' ) ) | |
or die "Cannot create socket: $!"; | |
# 2. ソケット情報の作成 | |
# 接続先のホスト名 | |
my $remote_host = 'localhost'; | |
my $packed_remote_host = inet_aton( $remote_host ) | |
or die "Cannot pack $remote_host: $!"; | |
# 接続先のポート番号 | |
my $remote_port = 9000; | |
print("${remote_host}:${remote_port}..."); | |
# ホスト名とポート番号をパック | |
my $sock_addr = sockaddr_in( $remote_port, $packed_remote_host ) | |
or die "Cannot pack $remote_host:$remote_port: $!"; | |
# 3. ソケットを使って接続 | |
connect( $main::sock, $sock_addr ) | |
or die "Cannot connect $remote_host:$remote_port: $!"; | |
print("connected\n"); | |
} | |
sub stdio { | |
my $i=0; | |
system("stty raw -echo"); | |
{ | |
my $char = ''; | |
while ($char eq '') { | |
$char = getc(); | |
threads->yield(); | |
} | |
if ($char =~ /[]/) { | |
print "\n"; | |
&disconnect(); | |
threads->exit(); | |
} | |
else { | |
$queue->enqueue($char); | |
#$queue->enqueue(undef); | |
redo; | |
} | |
} | |
} | |
sub tx { | |
# 4. データの書き込み | |
my $old_handle = select $main::sock; | |
$| = 1; | |
select $old_handle; | |
while (my $in = $queue->dequeue()) { | |
#print $main::sock "$in\n"; | |
send($main::sock,$in,0); | |
threads->yield(); | |
} | |
} | |
sub rx { | |
# 5. データの読み込み | |
while (my $receive_buf = getc($main::sock)) { | |
#print $sock_client $receive_buf; | |
print $receive_buf; | |
threads->yield(); | |
} | |
} | |
sub disconnect { | |
shutdown $main::sock, 1; # 書き込みを終了する。 | |
# 6. ソケットを閉じる | |
close $main::sock; | |
print "Disconnected.\n"; | |
system("stty sane"); | |
return; | |
} | |
__END__ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# echo server | |
# keiya_21@yahoo.co.jp | |
# thanks to | |
# main socket program http://d.hatena.ne.jp/perlcodesample/20100512/1276960096 | |
# fork program http://x68000.q-e-d.net/~68user/net/ | |
use strict; | |
use warnings; | |
use Socket; | |
$| = 1; | |
# 1. 受付用ソケットの作成 | |
my $sock_receive; | |
socket( $sock_receive, PF_INET, SOCK_STREAM, getprotobyname( 'tcp' ) ) | |
or die "Cannot create socket: $!"; | |
# 2. 受付用ソケット情報の作成 | |
my $local_port = 9000; | |
my $pack_addr = sockaddr_in( $local_port, INADDR_ANY ); | |
# 3. 受付用ソケットと受付用ソケット情報を結びつける | |
bind( $sock_receive, $pack_addr ) | |
or die "Cannot bind: $!"; | |
# 4. 接続を受け付ける準備をする。 | |
listen( $sock_receive, SOMAXCONN ) | |
or die "Cannot listen: $!"; | |
print '['.$$.']Started parent process on port '.$local_port."\n"; | |
my $sock_client; | |
# 5. 接続を受け付けて応答する。 | |
while(1) { | |
my $paddr = accept($sock_client, $sock_receive); | |
# ホスト名、IPアドレス、クライアントのポート番号を取得 | |
my ($client_port, $client_iaddr) = unpack_sockaddr_in($paddr); | |
my $client_hostname = gethostbyaddr($client_iaddr, AF_INET); | |
my $client_ip = inet_ntoa($client_iaddr); | |
print '['.$$.']Connection from: '.$client_ip.':'.$client_port."\n"; | |
# forkで子プロセスを生成 | |
if (my $pid = fork()){ | |
# こちらは親プロセス | |
# 親プロセスはソケットをクローズ | |
close($sock_client); | |
next; | |
} else { | |
# こっちは子プロセス | |
print '['.$$."]Started child process\n"; | |
# クライアントに対してバッファリングしない | |
my $old_handle = select $sock_client; | |
$| = 1; | |
select $old_handle; | |
print "\n"; | |
while (my $receive_buf = getc($sock_client)) { | |
print($receive_buf); | |
# クライアントにメッセージを返す | |
send($sock_client,$receive_buf,0); | |
} | |
print "\n"; | |
close($sock_client); | |
print '['.$$."]: Disconnect, process exit.\n"; | |
# ポートの監視は親プロセスが行っているので、 | |
# クライアントとのやりとりが終了すれば exit | |
exit; | |
} | |
} | |
__END__ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment