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
답변 감사합니다.
말씀해주신 자료 참고해서 한번 해보겠습니다. :-)
댓글 달기