PhillyR has asked for the wisdom of the Perl Monks concerning the following question:

First, I'm new to perl

I'm attempting to create a proxy server that will listen to data coming in on one socket (A) and then be able to provide that incoming data to multiple client sockets (Bs) but need some help in implementation.

My problem is I'm going to have 3 loops:
1: Receive data on A
2: Listen for new client connections
3: Provide data received on A to each B client socket

I've tried:
1: Forking with pipes from parent to child and even grandchildren that ended up providing data to the B clients in a round robin fashion
2: Broadcast UDP - clients cannot read from broadcast IP address

Currently I am trying to use IO::Select where the child will add each accepted client socket to an array. In my ideal world the parent would read through each item in the array and write the same data to each socket. But the parent doesn't have access items written by the child in this scenario.

use IO::Socket; use IO::Select; use IO::Handle; $A = new IO::SOCKET::INET (LocalHost => 'localhost', LocalPort => 1234, Type => SOCK_STREAM, Proto => "tcp", Reuse => 1, Listen => 5) or die "A Server socket couldn't be created: $@\n"; $B_listen = new IO::SOCKET::INET (LocalHost => 'localhost', LocalPort => 5555, Type => SOCK_STREAM, Proto => "tcp", Reuse => 1, Listen => 5) or die "B Server socket couldn't be created: $@\n"; my $B_clients = new IO::Select(); my $pid = fork(); if ($pid) { #PARENT while($A_sock = $A->accept()) { #Will only have 1 connection while(defined($buf=<$A_sock>)){ my @client_set = $B_clients->can_write; foreach my $client (@client_set) { print $client $buf; } } } } else { #CHILD while($B_sock = $B_listen->accept()) { $B_clients->add($B_sock); } }
Any ideas or different strategies?

Replies are listed 'Best First'.
Re: Proxy Server Help
by locked_user sundialsvc4 (Abbot) on Aug 23, 2011 at 14:32 UTC

    Good ol’ select() is probably your best friend here, and a relatively simple design.   If you think about it, a proxy is always just doing one of three things:   it’s processing a packet that has arrived; or it is processing a connect-request; or, it is snoozing, waiting for one of those other two things to happen.   The amount of CPU-time required to deal with any of these eventualities is virtually non-existent.   No matter how many “active connections” there might be, all of those are simply destinations.   They don’t add to the actual workload, which simply consists of connect-requests and packets.

    So, the app really isn’t “multi-threaded” at all.   It’s a simple linear loop, executing in an entirely I/O-constrained fashion that, from the CPU’s point-of-view, isn’t even particularly “fast.”   Like a dutiful mail-clerk, when a letter arrives, drop copies of it into the appropriate outbound slots, and when a connect request arrives, manufacture another slot.   Rinse and repeat.   Work for a few microseconds, then sleep for milliseconds.

      Thanks! I reinvestigated the proper use of select and found I was using it wrong. My code now works without forking or pipes or anything other than Select!

      use IO::Socket; use IO::Select; use IO::Handle; $A_lsn = new IO::SOCKET::INET (LocalHost => 'localhost', LocalPort => 1234, Type => SOCK_STREAM, Proto => "tcp", Reuse => 1, Listen => 5) or die "A Server socket couldn't be created: $@\n"; $B_lsn = new IO::SOCKET::INET (LocalHost => 'localhost', LocalPort => 5555, Type => SOCK_STREAM, Proto => "tcp", Reuse => 1, Listen => 5) or die "B Server socket couldn't be created: $@\n"; my $sockets = new IO::Select(); $sockets->add($A); $sockets->add($B); while(@ready = $sockets->can_read) { foreach $sockets (@ready) { if ($sockets == $A_lsn) { #new A client (sends data) $A_sock = $A_lsn->accept() $sockets->add($A_sock); } elsif($sockets == $B_lsn) {#new B client (recv data) $B_sock = $B_lsn->accept(); $sockets->add($B_sock); } elsif($sockets == $A_sock) #A client sent data, forward to all B clients $buf = <$A_sock> my @writers = $sockets->can_write; foreach $writer (@writers) { if ($writer == $A_sock) { #ignore } else { print $writer $buf; } } }