Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Problem extracting socket from fileno

by Anonymous Monk
on Mar 19, 2009 at 23:03 UTC ( [id://751902]=perlquestion: print w/replies, xml ) Need Help??

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

Hi there, Here is my code - use strict; use warnings; use Foo; my $object = new Foo; package Foo; use Bar; sub new { my $class = shift; my $self : shared; $self = &share({}); my $socket = new IO::Socket::INET ( PeerAddr => "localhost", PeerPort => 5250, Proto => 'tcp' ); if(defined $socket) { $self->{socketFileNo} = $socket->fileno; my $response; # eat up the welcome messages from the server my $response = <$socket>; print "Echo:",$response; $response = <$socket>; print "Echo:",$response; my $job = new Bar($self->{socketFileNo}); $job->start; #print "UADD 10101\n"; #print $socket "UADD 10101\n"; #$response = <$socket>; #print $response; #$response = <$socket>; #print $response; #print "LOAD U8 filename\n"; #print $socket "LOAD U8 filename\n"; #$response = <$socket>; #print "\tEcho: $response\n"; } else { $self->{socketFileNo} = undef; } bless $self, $class; return $self } 1; package Bar; use strict; use warnings; use threads; use threads::shared; use IO::Socket; sub new { my $class = shift; my $self : shared; $self = &share({}); $self->{socketFileNo} = shift; bless $self, $class; return $self; } # start the job sub start { my $self = shift; my $socket = undef; if(defined $self->{socketFileNo}) { open $socket, '+<&=' . $self->{socketFileNo}; } if(defined $socket) { my $response; print "UADD 10101\n"; print $socket "UADD 10101\n"; $response = <$socket>; print $response; $response = <$socket>; print $response; print "LOAD U8 filename\n"; print $socket "LOAD U8 filename\n"; $response = <$socket>; print "\tEcho: $response\n"; } } 1; This code produces the following output - Echo: Welcome Echo: Use 'HELP' for help. UADD 10101 202 OK U19 LOAD U8 filename Echo: 406 Command not supported I do not expect the server to reply with - "Echo: 406 Command not supported". However, if I comment out the two lines above concerned with the crea +tion of the Bar object and uncomment the lines which follow, I get - Echo: Welcome Echo: Use 'HELP' for help. UADD 10101 202 OK U19 LOAD U8 filename Echo: 406 Sucessful This is despite the fact that $job->start; should be doing the same th +ings as these uncommented lines. I'm guessing the problem is this line - "open $socket, '+<&=' . $self- +>{socketFileNo};" This is a very cut down version of my code so that I could isolate the + problems. I'm using threads normally, however as you can see I get t +he problem regardless. What might the problem be? It seems I can communicate to and from the +server via $job->start but that things are getting corrupted. Please help, Thanks in advance.

Replies are listed 'Best First'.
Re: Problem extracting socket from fileno
by almut (Canon) on Mar 20, 2009 at 03:00 UTC

    As no one has said anything so far, I'll just give it a shot... :)

    One potential problem here could be that the "socket" you open to the real socket's file descriptor is not really an IO::Socket::INET object/handle, but rather a regular filehandle. As such it doesn't know about the autoflush setting of the IO::Socket handle (for example), not stored within the descriptor.

    Autoflush is automatically turned on by IO::Socket (as of version 1.18). The other handle, however, will not "inherit" this setting, so in the one case (using the original socket handle) it's on, but in the other case it's off. This might account for different behaviour...   Proof-of-concept demo:

    #!/usr/bin/perl use strict; use warnings; use IO::Socket; my $pid = fork(); die "couldn't fork" unless defined($pid); if ($pid) { # SERVER my $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => 9999, Listen => 1, ReuseAddr => 1, ); die "couldn't setup server" unless $server; my $client = $server->accept(); $client->autoflush(1); while (<$client>) { if (/hello/) { print $client "nice to meet you\n" } elsif (/quit/) { print $client "bye\n"; last } else { print $client "??\n" } } close $client; } else { # CLIENT sleep 1; # wait for server to get ready my $sock1 = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => "localhost", PeerPort => 9999, ); die "couldn't connect" unless $sock1; open my $sock2, '+<&='.$sock1->fileno() or die $!; printf "sock1 = %s\n", $sock1; printf "fileno(sock1) = %d\n", $sock1->fileno(); printf "autoflush = %s\n\n", $sock1->autoflush() ? "ON":"OFF"; printf "sock2 = %s\n", $sock2; printf "fileno(sock2) = %d\n", $sock2->fileno(); printf "autoflush = %s\n\n", $sock2->autoflush() ? "ON":"OFF"; my $resp; print $sock2 "hello\n"; $resp = <$sock2>; print $resp; print $sock2 "quit\n"; $resp = <$sock2>; print $resp; exit; }

    As it is, it should print something like

    $ ./751902.pl sock1 = IO::Socket::INET=GLOB(0x7dc230) fileno(sock1) = 4 autoflush = ON sock2 = GLOB(0x604410) fileno(sock2) = 4 autoflush = OFF nice to meet you bye

    But if you comment out the line with the $sock2->autoflush(), it will hang before "nice to meet you", because the client's print $sock2 "hello\n"; doesn't make it to the server, due to not being flushed. (Note that ->autoflush() is not a real "getter" method — i.e. it does return the current state, but it's special in that it also turns it on! So, despite the printout "OFF", it is in fact ON after the call... unless you comment out that line — you get the idea.)

    I have no real explanation how these circumstances would produce the exact behaviour you observe, but that's roughly where I would start looking (e.g. try $socket->autoflush() after the open $socket, '+<&='...).

    If that doesn't help, you could use strace to figure out what's actually being exchanged over the socket.

    HTH.

    BTW, is there any specific reason you can't simply pass around the original $socket itself, instead of its file descriptor number?

      >>BTW, is there any specific reason you can't simply pass around the original $socket itself, instead of its file descriptor number?

      Yes, I'm trying to share it between threads and I get a error along the lines of "sharnig of GLOB not implemented yet" when I try to share it via self.
      Thanks for the reply! Your script gave me - sock1 = IO::Socket::INET=GLOB(0xdcf36c) fileno(sock1) = 4 autoflush = ON sock2 = GLOB(0xdcee1c) fileno(sock2) = 4 autoflush = OFF nice to meet you ?? I used $socket->autoflush() to switch on autoflush and then checked th +at it was indeed turned on. I'm still getting the exact same error th +ough. And other suggestions? Thanks again!
      </code>
      Ok I've rewriten your script as follows -
      #!/usr/bin/perl use strict; use warnings; use IO::Socket; my $pid = fork(); die "couldn't fork" unless defined($pid); if ($pid) { # SERVER my $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => 9999, Listen => 1, ReuseAddr => 1, ); die "couldn't setup server" unless $server; my $client = $server->accept(); $client->autoflush(1); while (<$client>) { print "DUMP:\n"; use Data::Dumper; print Dumper($_); print "DUMP END\n"; if (/hello/) { print $client "nice to meet you\n" } elsif (/quit/) { print $client "bye\n"; } else{ print $client "??\n"; } } close $client; } else { # CLIENT sleep 1; # wait for server to get ready my $sock1 = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => "localhost", PeerPort => 9999, ); die "couldn't connect" unless $sock1; open my $sock2, '+<&='.$sock1->fileno() or die $!; printf "sock1 = %s\n", $sock1; printf "fileno(sock1) = %d\n", $sock1->fileno(); printf "autoflush = %s\n\n", $sock1->autoflush() ? "ON":"OFF"; printf "sock2 = %s\n", $sock2; printf "fileno(sock2) = %d\n", $sock2->fileno(); printf "autoflush = %s\n\n", $sock2->autoflush() ? "ON":"OFF"; my $resp; print $sock1 "hello\n"; $resp = <$sock1>; print $resp; print $sock1 "quit\n"; $resp = <$sock1>; print $resp; exit; }
      And when I print out the values for sock1 I get -
      sock1 = IO::Socket::INET=GLOB(0xd9327c) fileno(sock1) = 4 autoflush = ON sock2 = GLOB(0xe424b4) fileno(sock2) = 4 autoflush = OFF DUMP: $VAR1 = 'hello '; DUMP END nice to meet you DUMP: $VAR1 = 'quit '; DUMP END bye
      But if I print for socket2 (by changing the last five lines of the script) I get -
      sock1 = IO::Socket::INET=GLOB(0xd93504) fileno(sock1) = 4 autoflush = ON sock2 = GLOB(0xe42714) fileno(sock2) = 4 autoflush = OFF DUMP: $VAR1 = 'hello '; DUMP END nice to meet you DUMP: $VAR1 = 'nice to meet you '; DUMP END ?? DUMP: $VAR1 = 'quit '; DUMP END
      Where is that extra message comming from. Am I not really flushing?
      Thanks again!

        Generally, what you're trying to do here is only borderline supported (if at all!). There's a reason why sockets aren't created with open — the underlying file descriptor is only part of what the OS needs to operate a socket.  And I'd wager it's one of those subtle differences (to regular file handles) that are responsible for the problems you're observing. (This is also known as "In Unix everything is a file ...unless it isn't" ;) such as sockets.)

        In this specific case, it looks like the output the server has written ("nice to meet you") is immediately fed back to itself, so its own output gets in the way of new input...  (I can't replicate this behaviour here, btw, but that's probably just due to minor timing differences on my system, or whatever).

        In short, it's probably best to give up that route, and try to find some other way of implementing it.  What's the bigger picture of things you're trying to accomplish in the end?  Maybe people can suggest alternative approaches...

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (2)
As of 2025-05-17 03:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.