Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

Re^3: Can a socket listen for multiple hosts?

by ikegami (Patriarch)
on Jan 22, 2010 at 21:25 UTC ( #819053=note: print w/replies, xml ) Need Help??

in reply to Re^2: Can a socket listen for multiple hosts? -- UPDATE
in thread Can a socket listen for multiple hosts?

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.


#!/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; } } } } }

Replies are listed 'Best First'.
Re^4: Can a socket listen for multiple hosts? -- UPDATE
by sierpinski (Chaplain) on Jan 27, 2010 at 17:57 UTC
    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.

Re^4: Can a socket listen for multiple hosts?
by sierpinski (Chaplain) on Feb 01, 2010 at 13:36 UTC
    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)

    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
      $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
        I found this thread while super-searching on another Socket question I had, and realized I never updated it with how I ended up achieving this. I user Data::Dumper to flatten the hash into a scalar, sent that to the server, then used eval to turn it back into a hash, and it worked flawlessly.

        Of course now I'm trying the educational task of creating a two-way client-server app using IO::Socket without going back and copy/pasting a bunch of code. I'm learning much more this way, but it's taking me a bit longer to get it working. Thanks again for the responses ikegami!

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2023-06-06 17:22 GMT
Find Nodes?
    Voting Booth?
    How often do you go to conferences?

    Results (29 votes). Check out past polls.