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

I seem to have run into a platform discrepency between win32/*nix... yet strangely, the script in question works on win32, but not on *nix! Here is the situation: I Have a server, which may or may not be running on the local host. This server stores data. When the server is connected to, it recieves data. First, the header is chopped off and validated; if it is valid, the data is then examined. If the data is actually a send request, the stored data is sent back to the client; otherwise, the data is stored. This system worked great on win32, and also on darwin. However, when I tried debugging on *nix, my retrieve method broke. It was originally this:

sub retrieve { my ($self, $var) = @_; $var = $self->{vars}->{$var->{name}.$var->{ref}}; my $address = exists($$var->{address}) ? $$var->{address} : '127.0 +.0.1'; my $message = IO::Socket::INET->new ( Proto => 'tcp', PeerAddr => $address, PeerPort => $$var->{port}, ) or die ( $self->cleanup($!) ); $message->autoflush(1); my $port = $message->sockport; if ($$var->{debug}) { print "Connected to ", $$var->{ref}, " for retrieving:\n"; print "\tPeerhost: ", $message->peerhost, "\n"; print "\tPeerport: ", $message->peerport, "\n"; print "\tLocalhost: ", $message->sockhost, "\n"; print "\tLocalport: ", $message->sockport, "\n\n"; } my $header = crypt(crypt($$var->{ref},$$var->{ref}),$$var->{ref}); syswrite($message, $header."\bl\b", 3+length($header)); $message->close; $message = IO::Socket::INET->new ( Listen => SOMAXCONN, LocalPort => $port, Reuse => 1, LocalAddr => '127.0.0.1', ) or croak ( $self->cleanup($!) ); if ($$var->{debug}) { print "Listening for ", $$var->{ref}, ":\n"; print "\tLocalport: ", $message->sockport, "\n\n"; } while (my $connection = $message->accept) { if ($$var->{debug}) { print "Recieved a connection from ", $$var->{ref}, ":\n"; print "\tPeerhost: ", $connection->peerhost, "\n"; print "\tPeerport: ", $connection->peerport, "\n"; print "\tLocalhost: ", $connection->sockhost, "\n"; print "\tLocalport: ", $connection->sockport, "\n\n"; } my $sent = <$connection>; $connection->close if $connection; $sent = join('',map(chr,split(/\*/,$sent))); $sent = thaw($sent); $message->close; return $$sent; } $message->close if $message; }

However, this produced the error that the "address was already in use" (meaning the port was already in use) when I tried to open the listening socket; however, I had closed the socket a few lines earlier. Next, I remembered that sockets were bi-directional, so I tried to use the the same socket that I connected with to retrieve; however, this attempt caused the socket to hang:

sub retrieve { my ($self, $var) = @_; $var = $self->{vars}->{$var->{name}.$var->{ref}}; my $address = exists($$var->{address}) ? $$var->{address} : '127.0 +.0.1'; my $message = IO::Socket::INET->new ( Proto => 'tcp', PeerAddr => $address, PeerPort => $$var->{port}, ) or die ( $self->cleanup($!) ); $message->autoflush(1); my $header = crypt(crypt($$var->{ref},$$var->{ref}),$$var->{ref}); syswrite($message, $header."\bl\b", 3+length($header)); my $sent = <$message>; $sent = join('',map(chr,split(/\*/,$sent))); $sent = thaw($sent); $message->close if $message->connected; return $$sent;
Finally, my final attempt was similar to my first, with the exception that I opened my listening socket before the outgoing socket, and the port number the incoming was listening on sent along with the "send request" so that the server could connect to it. However, the server never got that far as opening the listening socket first caused the send to never occur:
sub retrieve { my ($self, $var) = @_; $var = $self->{vars}->{$var->{name}.$var->{ref}}; my $address = exists($$var->{address}) ? $$var->{address} : '127.0 +.0.1'; my $incoming = IO::Socket::INET->new ( Listen => SOMAXCONN, LocalAddr => '127.0.0.1', Reuse => 1, ) or croak ( $self->cleanup($!) ); my $port = $incoming->sockport; $incoming->shutdown(1); my $message = IO::Socket::INET->new ( Proto => 'tcp', PeerAddr => $address, PeerPort => $$var->{port}, ) or die ( $self->cleanup($!) ); $message->autoflush(1); my $header = crypt(crypt($$var->{ref},$$var->{ref}),$$var->{ref}); syswrite($message, $header."\bl\b".$port, 3+length($header)+length +($port)); my $sent = <$connection>; $sent = join('',map(chr,split(/\*/,$sent))); $sent = thaw($sent); $incoming->close if $incoming->connected; $message->close if $message->connected; $connection->close if $connection->connected; return $$sent; }

I'm nearly at the end of my rope here; I've spent many hours debugging and tweaking and I'm still back at square one. I've tested on several different *nix platforms (solaris, red hat, caldera, debian) yet all produced the same error; why doesn't it work on *nix yet it works with both win32 and darwin?

Replies are listed 'Best First'.
Re (tilly) 1: Socket Differences between Win32/Darwin and *nix.
by tilly (Archbishop) on Jan 10, 2002 at 16:44 UTC
    I haven't done very much socket programming, but I suspect that an issue is that on Unix, a bound socket is not immediately released when you quit. (I have certainly run into behaviour like this before.) Therefore there is a (possibly considerable) delay after successfully binding and then quitting before anything else can successfully bind to that port again.

    Therefore I would suggest organizing your server in a more traditional way. Rather than returning out of your accept loop, plan to get into an accept loop and then stay there servicing more requests until your program is done.

      Excellent articles on this delay after closing a socket on a bound port are linked from here.

      OP: Read the FAQ articles (some are linked in the follow-ups) and if you decide that you want to bind on the port again immediately, use setsockopt and monkey with the SO_REUSEADDR option.

        Or simply add Reuse=>1 to your IO::Socket::INET->new call.

        Update: ...which was already in the original code. Which makes me wonder how this particular error could happen...

                - tye (but my friends call me "Tye")