Perl로 windows에서 Client/Server 작성시 fork/thread 사용 질문드려요.
안녕하세요.
현재 camelbox(http://code.google.com/p/camelbox/)를 Windows XP에 설치하였고요,
IO::Socket과 socket 두 버전으로 코딩을 했는데요.
둘 다 fork를 하기 전에는 cmd.exe 창을 통해서 잘 실행이 되는데
fork를 적용한 경우 cmd.exe 창에서는 데이터가 전송되지 않고 멈춘것처럼 보입니다.
Cygwin에서 같은 코드를 실행시킬 경우에는 잘 됩니다....
처음에는 flushing 문제라 생각하고 처리해 주었는데요, (http://coding.derkeiler.com/Archive/Perl/comp.lang.perl.misc/2005-05/msg01045.html)
fork 만 사용하면 데이터 전송이 멈춘것처럼 동작해서요...
Cygwin에서 동작하는 걸로 봐서 Windows에서만 발생하는 문제인것 같은데,
(http://win32.perl.org/wiki/index.php?title=Compatibility_List_of_Perl_Modules#IO::Socket)
위 사이트에 보면 IO::Socket 의 경우 버그가 있다고는 하는데요.
그렇다면 listen,bind,accept,socket등의 함수를 사용한 버전은 동작해야 한다고 생각했는데 여전히 같은 현상이 발생하네요...
Windows에서 socket과 fork를 동시에 사용한 프로그래밍이 불가능한건가요?
조언 부탁드립니다.
아래는 테스트한 Server/Client 프로그램 코드입니다.
#!/bin/perl -w
require 5.002;
use strict;
use Socket;
use POSIX qw(:sys_wait_h);
use Carp;
use Sys::Hostname;
sub REAPER {
1 until (-1 == waitpid(-1, WNOHANG));
$SIG{CHLD} = \&REAPER;
}
sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }
my $port = shift || 2345;
my $proto = getprotobyname('tcp');
my $kid;
socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
listen(Server, SOMAXCONN) or die "listen: $!";
logmsg "server started on port $port";
my $paddr;
$SIG{CHLD} = \&REAPER;
$| = 1;
while ( $paddr = accept(Client, Server)) {
my ($port, $iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr, AF_INET);
logmsg "connection from $name [", inet_ntoa($iaddr), "] at port $port";
my $old_fh = select(Client);
$| = 1;
select($old_fh);
# execute a fork, if this is the parent,
# its work is done, go straight to continue
next if $kid = fork;
die "fork: $!" unless defined $kid;
# child now...
# close the server - not needed
close Server;
print Client "Hello there, $name, it\'s now ", scalar localtime, "\n";
my $buf;
while (defined($buf = <Client>)) {
foreach ($buf) {
if (/^HELLO$/) {
print Client "Hi\n";
last;
}
elsif (/^NAME$/) {
print Client hostname(), "\n";
last;
}
elsif (/^DATE$/) {
print Client scalar(localtime), "\n";
last;
}
# default case:
print Client "DEFAULT\n";
}
}
close Client;
}#!/bin/perl -w
require 5.002;
use strict;
use Socket;
my ($remote, $port, $iaddr, $paddr, $proto, $line);
my ($in, $buf, $kid);
$remote = shift || 'localhost';
$port = shift || 2345; # random port
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp') }
die "No port" unless $port;
$iaddr = inet_aton($remote) or die "no host; $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(SOCK, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";
connect(SOCK, $paddr) or die "connect: $!";
my $old_fh = select(SOCK);
$| = 1;
select($old_fh);
die "fork fail: $!" unless defined($kid = fork);
if ($kid) {
# parent reads from STDIN, prints to socket
while (defined($in = <STDIN>)) {
print SOCK $in;
}
# kill the child process
kill(TERM => $kid);
} else {
# child reads from socket, prints to STDOUT
while (defined($buf = <SOCK>)) {
print $buf;
}
close SOCK;
}
exit;

Perl로 네트웍 프로그래밍 하시려면
일단 윈도우용 Perl의 대세는 딸기펄 ( http://strawberryperl.com/ ) 입니다.
시스템 콜관련 부분은 UNIX(linux), Win32 간에 포팅이 되었다고 해도 미묘하게 동작이 다른 부분이 있어
저수준(?)으로 구현하면 여러가지로 예외상황이나 신경써야 될 부분이 많을 수 있습니다.
이때는 그러한 층을 Wrapping해서 좀 더 고수준에서 편하게 쓸 수 있게 해주는 모듈들을 사용해 보세요.
POE - http://poe.perl.org/
AnyEvent - http://search.cpan.org/dist/AnyEvent/
요즘 뜨는
Coro - http://search.cpan.org/dist/Coro/
기타 참고할만한 글들
http://d.hatena.ne.jp/tokuhirom/20090630/1246324092
http://j2k.naver.com/j2k_frame.php/korean/d.hatena.ne.jp/tokuhirom/20090629/1246239835
답변 감사합니다.
말씀해주신 자료 참고해서 한번 해보겠습니다. :-)
댓글 달기