Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re^2: Can a socket listen for multiple hosts? -- UPDATE

by sierpinski (Chaplain)
on Jan 22, 2010 at 20:53 UTC ( [id://819051]=note: print w/replies, xml ) Need Help??


in reply to Re: Can a socket listen for multiple hosts?
in thread Can a socket listen for multiple hosts?

Thanks for the responses. I ended up using IO::Select, and got it working. Posting the code here for posterity/review. Thanks!
#!/usr/bin/perl use IO::Socket; use IO::Select; my $socket =new IO::Socket::INET->new ( LocalHost => "<ipaddress>", LocalPort => '<port>', Proto => 'tcp', Listen => 1, Reuse => 1, ); die("Couldn't create socket! $!\n") unless $socket; my $select = new IO::Select($socket); while(@ready = $select->can_read) { foreach $fh (@ready) { if($fh == $socket) { $new = $socket->accept; $select->add($new); my $host= $new->peerhost; print "[Accepting connection from $host]\n"; } else { my $line = <$fh>; $line =~ s/\s+$//; if($line =~/^quit$/i) { my $host = $fh->peerhost; $select->remove($fh); $fh->close; print "[Connection from $host terminat +ed\n"; } else { print $fh->peerhost, " said '$line'\n" +; print $fh "You said: '$line'\n"; } } } }
Thanks for all the help! With multiple servers sending at the same time, it keeps track (using peerhost) which host sends the message, and of course the message contents.
/\ Sierpinski

Replies are listed 'Best First'.
Re^3: Can a socket listen for multiple hosts?
by ikegami (Patriarch) on Jan 22, 2010 at 21:25 UTC

    Don't use buffered IO (e.g. read, readline aka <>) with select. You could get into a situation where data is waiting in Perl's buffer, and select wouldn't know anything about it.

    Don't use IO that blocks after it reads all that's available to be read (e.g. read, readline aka <>) with select. It defies the purpose of using select.

    You don't handle EOF, so you could end up having a handle that's permanently ready to read.

    Fixed:

    #!/usr/bin/perl use strict; use warnings; use IO::Socket::INET qw( ); use IO::Select qw( ); sub process_msg { my ($client, $msg) = @_; chomp $msg; my $host = $client->peerhost; print "$host said '$msg'\n"; return lc($msg) eq 'quit; } my $server = IO::Socket::INET->new( ... ) or die("Couldn't create server socket: $!\n"); my $select = IO::Select->new($server); my %bufs; while (my @ready = $select->can_read) { for my $fh (@ready) { if ($fh == $server) { my $client = $server->accept; $select->add($client); $bufs{fileno($client)} = ''; my $host = $client->peerhost; print "[Accepted connection from $host]\n"; } else { our $buf; local *buf = \$bufs{fileno($fh)}; my $rv = sysread($fh, $buf, 64*1024, length($buf)); if (!$rv) { my $host = $fh->peerhost; if (defined($rv)) { print "[Connection from $host terminated]\n"; } else { print "[Error reading from host $host: $!]\n"; } process_msg($fh, $buf) if length($buf); delete $bufs{fileno($fh)}; $sel->remove($fh); next; } while ($buf =~ s/\G(.*\n)//g) { if (!process_msg($fh, "$1")) { my $host = $fh->peerhost; print "[Connection from $host terminated]\n"; delete $bufs{fileno($fh)}; $sel->remove($fh); last; } } } } }
      Thanks for the response! I knew it was a good idea to post my code here. I'm using the updated version, and it is working. I just need to go through and make sure I understand what the differences are... definitely a learning experience.

      Thanks again!
      /\ Sierpinski

        Your code relies on the assumptions that the pipe never contains more than one message and that it always contains complete messages.

        The difference is that my code doesn't rely on those bad assumptions.

      EDIT: Not sure what I was thinking, Monday morning I guess. I just replaced the \n chars with something else, searched for that after the transmission, then replaced them with \n's again afterward. It works great.

      Ikegami, thanks again for the response.
      I've been trying to decode this, and I think it mostly makes sense to me, but I need to be able to send multi-line messages (ie \n chars embedded) and want to terminate the sending connection with a specific character string. In other words, I'll have a datagram that looks something like this:

      &&&hostname|blue*** (the start/end chars can be anything)
      &&&ipaddress|1.2.3.4***
      &&&kernelrev|123456-78***
      &&&metastat|line1\nline2\nline3\n***

      So I basically will be sending all types of data, but I want it to start paying attention when &&& is read, and read until ***, storing the contents in a var. (Probably will end up using a hash, if it matters)

      I thought I had a good grip on how exactly that code works, but when I change what I thought I should change, it doesn't work as I expected. Any thoughts to send me in the right direction?

      Much appreciated!
      /\ Sierpinski
        Adjust
        $buf =~ s/\G(.*\n)//g
        to match your definition of record. If your record terminator is "***", you can use
        $buf =~ s/\G(.*?\*\*\*)//sg

        If your records are double quoted strings, you can use

        $buf =~ s/\G("(?:[^"\\]|\\.)*")//sg

        If you're using fixed-length records, you can use

        $buf =~ s/\G(.{128})//sg
        etc.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://819051]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (7)
As of 2024-04-24 10:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found