Presentation is loading. Please wait.

Presentation is loading. Please wait.

Perl and UNIX Network Programming

Similar presentations


Presentation on theme: "Perl and UNIX Network Programming"— Presentation transcript:

1 Perl and UNIX Network Programming
Naoya Ito naoya at hatena.ne.jp

2 Why now network programming?
httpd is boring Some recent web application have special feature of networking. Comet Socket API of ActionScript 3 mini server for development, like Catalyst's server.pl

3 Agenda UNIX network programming basics with Perl I/O multiplexing
Perl libraries for modern network programming

4 UNIX network programming basics with Perl

5 BSD Socket API with C int main (void) { int listenfd, connfd;
struct sockaddr_in servaddr; char buf[1024]; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(9999); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listenfd, 5); for (;;) { connfd = accept(listenfd, NULL, NULL) ; while (read(connfd, buf, sizeof(buf)) > 0) { write(connfd, buf, strlen(buf)); } close(connfd);

6 BSD Socket API socket() struct sockaddr_in bind() listen() accept()
read() / write() close()

7 Perl Network Programming
TMTOWTDI less code CPAN performance is good enough right design >> ... >> language advantage

8 BSD Socket API with Perl
#!/usr/local/bin/perl use strict; use warnings; use Socket; socket LISTEN_SOCK, AF_INET, SOCK_STREAM, scalar getprotobyname('tcp'); bind LISTEN_SOCK, pack_sockaddr_in(9999, INADDR_ANY); listen LISTEN_SOCK, SOMAXCONN; while (1) { accept CONN_SOCK, LISTEN_SOCK; while (sysread(CONN_SOCK, my $buffer, 1024)) { syswrite CONN_SOCK, $buffer; } close CONN_SOCK;

9 use IO::Socket #!/usr/local/bin/perl use strict; use warnings;
my $server = IO::Socket::INET->new( Listen => 20, LocalPort => 9999, Reuse => 1, ) or die $!; while (1) { my $client = $server->accept; while ($client->sysread(my $buffer, 1024)) { $client->syswrite($buffer); } $client->close; $server->close;

10 blocking on Network I/O
while (1) { my $client = $server->accept; while ($client->sysread(my $buffer, 1024)) { # block $client->syswrite($buffer); } $client->close; accept(2) server listen queue read(2) I can't do client #1 client #2

11 busy loop / blocking while (1) { $i++ }
% ps -e -o stat,pid,wchan=WIDE-WCHAN-COLUMN,time,comm while (1) { $i++ } STAT PID WIDE-WCHAN-COLUMN TIME COMMAND R :00:38 perl while (1) { STDIN->getline } STAT PID WIDE-WCHAN-COLUMN TIME COMMAND S read_chan :00:00 perl

12 Linux internals process TASK_RUNNING libc.so system call vfs
buffer fread() TASK_RUNNING libc.so buffer read(2) system call vfs TASK_UNINTERRUPTIBLE ext3 switch to Kernel-Mode. User-process goes sleep. device driver Kernel-Mode Hardware Interruption. Hardware (HDD) ref: 『Linux カーネル2.6解読室』 p.32

13 Again: blocking while (1) { my $client = $server->accept;
while ($client->sysread(my $buffer, 1024)) { # block $client->syswrite($buffer); } $client->close;

14 We need parallel processing
fork() threads Signal I/O I/O Multiplexing Asynchronous I/O

15 I/O multiplexing

16 I/O Multiplexing Parallel I/O in single thread, watching I/O event of file descripters less resource than fork/threads select(2) / poll(2) wait for a number of file descriptors to change status.

17 2. now listening socket is ready to accept a new connection.
select(2) accepted connection #2 listening socket accepted connection #1 1. ready! select(2) 3. ok, I'll try to accept() 2. now listening socket is ready to accept a new connection. caller

18 select(2) on Perl select(@args) IO::Select
number is not 1 but 4. difficult interface IO::Select OO interface to select(2) easy interface

19 IO::Select SYNOPSYS use IO::Select; $s = IO::Select->new();
$s->add(\*STDIN); $s->add($some_handle); @ready = $s->can_read($timeout); # block

20 use IO::Select my $listen_socket = IO::Socket::INET->new(...) or die my $select = IO::Select->new or die $!; $select->add($listen_socket); while (1) { = $select->can_read; # block for my $handle { if ($handle eq $listen_socket) { my $connection = $listen_socket->accept; $select->add($connection); } else { my $bytes = $handle->sysread(my $buffer, 1024); $bytes > 0 ? $handle->syswrite($buffer) : do { $select->remove($handle); $handle->close; }

21 And more things we must think...
blocking when syswrite() use non-blocking socket Line-based I/O select(2) disadvantage

22 non-blocking socket + Line-based I/O
use POSIX; use IO::Socket; use IO::Select; use Tie::RefHash; my $server = IO::Socket::INET->new(...); $server->blocking(0); my (%inbuffer, %outbuffer, %ready); tie %ready, "Tie::RefHash"; my $select = IO::Select->new($server); while (1) { foreach my $client ( $select->can_read(1) ) { handle_read($client); } foreach my $client ( keys %ready ) { foreach my $request $ready{$client} } ) { $outbuffer{$client} .= $request; delete $ready{$client}; foreach my $client ( $select->can_write(1) ) { handle_write($client); sub handle_error { my $client = shift; delete $inbuffer{$client}; delete $outbuffer{$client}; $select->remove($client); close $client; sub handle_read { my $client = shift; if ($client == $server) { my $new_client = $server->accept(); $new_client->blocking(0); $select->add($new_client); return; } my $data = ""; my $rv = $client->recv($data, POSIX::BUFSIZ, 0); unless (defined($rv) and length($data)) { handle_error($client); $inbuffer{$client} .= $data; while ( $inbuffer{$client} =~ s/(.*\n)// ) { $1; sub handle_write { return unless exists $outbuffer{$client}; my $rv = $client->send($outbuffer{$client}, 0); unless (defined $rv) { warn "I was told I could write, but I can't.\n"; if ($rv == length( $outbuffer{$client}) or $! == POSIX::EWOULDBLOCK) { substr( $outbuffer{$client}, 0, $rv ) = ""; delete $outbuffer{$client} unless length $outbuffer{$client}; oops

23 select(2) disadvantage
FD_SETSIZE limitation not good for C10K Inefficient processing coping list of fds to the kernel You must scan list of fds in User-Land

24 select(2) internals process select(2) select(2) FD_ISSET copy copy
I/O event kernel ref:

25 Modern UNIX APIs epoll Linux 2.6 /dev/kqueue BSD devpoll Solaris

26 epoll(4) better than select(2), poll(2)
no limitation of numbers of fds O(1) scallability needless to copy list of fds epoll_wait(2) returns only fds that has new event

27 epoll internals process kernel epoll_create() epoll_wait() fd table
epoll_ctl(ADD) epoll_ctl(ADD) epoll_ctl(ADD) fd table fd fd fd fd fd fd I/O event kernel ref:

28 epoll on perl Sys::Syscall IO::Epoll epoll sendfile
use IO::Epoll qw/:compat/

29 Perl libraries for modern network programming

30 Libraries for Perl Network Programming
TMTOWTDI POE Event::Lib Danga::Socket Event Stem Coro ...

31 Event-based programming for parallel processing
They provides: Event-based programming for parallel processing system call abstraction select(2) / poll(2) / epoll / kqueue(2) / devpoll

32 POE "POE is a framework for cooperative, event driven multitasking in Perl. " POE has many "components" on CPAN I'm lovin' it :)

33 Hello, POE use strict; use warnings; use POE qw/Sugar::Args/;
POE::Session->create( inline_states => { _start => sub { my $poe = sweet_args; $poe->kernel->yield('hello'), # async / FIFO }, hello => sub { STDOUT->print("Hello, POE!"); ); POE::Kernel->run;

34 Watching handles in Event loop
POE::Session->create( inline_states => { _start => sub { my $poe = sweet_args; $poe->kernel->yield('readline'), }, readline => sub { STDOUT->syswrite("input> "); $poe->kernel->select_read(\*STDIN, 'handle_input'); handle_input => sub { my $stdin = $poe->args->[0]; STDOUT->syswrite(sprintf "Hello, %s", $stdin->getline); $poe->kernel->yield('readline'); } );

35 Results % perl hello_poe2.pl input> naoya Hello, naoya
input> hatena Hello, hatena input> foo bar Hello, foo bar input>

36 Results of strace % strace -etrace=select,read,write -p `pgrep perl`
Process 8671 attached - interrupt to quit select(8, [0], [], [], {3570, }) = 1 (in [0], left {3566, }) read(0, "naoya\n", 4096) = 6 write(1, "Hello, naoya\n", 13) = 13 select(8, [0], [], [], {0, 0}) = 0 (Timeout) write(1, "input> ", 7) = 7 select(8, [0], [], [], {3600, 0}) = 1 (in [0], left {3595, }) read(0, "hatena\n", 4096) = 7 write(1, "Hello, hatena\n", 14) = 14 select(8, [0], [], [], {3600, 0}) = 1 (in [0], left {3598, }) read(0, "foobar\n", 4096) = 7 write(1, "Hello, foobar\n", 14) = 14 select(8, [0], [], [], {3600, 0}

37 use POE::Wheel::ReadLine
POE::Session->create( inline_states => { ... readline => sub { my $poe = sweet_args; $poe->heap->{wheel} = POE::Wheel::ReadLine->new( InputEvent => 'handle_input', ); $poe->heap->{wheel}->get('input> '); }, handle_input => sub { $poe->heap->{wheel}->put(sprintf "Hello, %s", $poe->args->[0]); }

38 Parallel echo server using POE
POE::Session->create( inline_states => { _start => \&server_start, }, package_states => [ main => [qw/ accept_new_client accept_failed client_input /], ] ); POE::Kernel->run; sub server_start { my $poe = sweet_args; $poe->heap->{listener} = POE::Wheel::SocketFactory->new( BindPort => 9999, Reuse => 'on', SuccessEvent => 'accept_new_client', FailureEvent => 'accept_failed', } sub accept_new_client { my $poe = sweet_args; my $wheel = POE::Wheel::ReadWrite->new( Handle => $poe->args->[0], InputEvent => 'client_input', ); $poe->heap->{wheel}->{$wheel->ID} = $wheel; } sub client_input { my $line = $poe->args->[0]; my $wheel_id = $poe->args->[1]; $poe->heap->{wheel}->{$wheel_id}->put($line); sub accept_failed {}

39 Again, Parallel echo server using POE
use POE qw/Sugar::Args Component::Server::TCP/; POE::Component::Server::TCP->new( Port => 9999, ClientInput => sub { my $poe = sweet_args; my $input = sweet_args->args->[0]; $poe->heap->{client}->put($input); }, ); POE::Kernel->run();

40 POE has many components on CPAN
PoCo::IRC PoCo::Client::HTTP PoCo::Server::HTTP PoCo::EasyDBI PoCo::Cron PoCo::Client::MSN PoCo::Client::Linger ...

41 using POE with epoll just use POE::Loop::Epoll
use POE qw/Loop::Epoll/;

42 Event::Lib libevent(3) wrapper libevent provides: Similar to Event.pm
libevent is used by memcached libevent provides: event-based programming devpoll, kqueue, epoll, select, poll abstraction Similar to Event.pm Simple

43 echo server using Event::Lib
my $server = IO::Socket::INET->new(...) or die $!; $server->blocking(0); event_new($server, EV_READ|EV_PERSIST, \&event_accepted)->add; event_mainloop; sub event_accepted { my $event = shift; my $server = $event->fh; my $client = $server->accept; $client->blocking(0); event_new($client, EV_READ|EV_PERSIST, \&event_client_input)->add; } sub event_client_input { my $client = $event->fh; $client->sysread(my $buffer, 1024); event_new($client, EV_WRITE, \&event_client_output, $buffer)->add; sub event_client_output { ... }

44 Result of strace on Linux 2.6
epoll_wait(4, {{EPOLLIN, {u32= , u64= }}}, 1023, 5000) = 1 gettimeofday({ , }, NULL) = 0 read(7, "gho\r\n", 1024) = 5 epoll_ctl(4, EPOLL_CTL_MOD, 7, {EPOLLIN|EPOLLOUT, {u32= , u64= }}) = 0

45 Danga::Socket by Brad Fitzpatrick - welcome to Japan :)
It also provides event-driven programming and epoll abstraction Perlbal, MogileFS

46 Summary For Network programming, need a little knowledge about OS, especially process scheduling, I/O and implementation of TCP/IP. Use modern libraries/frameworks to keep your codes simple. Perl has many good libraries for UNIX Network Programming.

47 Thank you!


Download ppt "Perl and UNIX Network Programming"

Similar presentations


Ads by Google