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

I have simple client and server scripts based on IO::Socket. Server just prints something into socket. Client just reads that from socket. But! If the server creates a subprocess (using system() call) before (!) closing client socket then the client will block on read until subprocess finishes. The point is that the server finishes immediately (subprocess is created that way). The problem occurs only if server is on Windows. BTW: I created the same sample on java and it works perfectly everywhere.

Server:

########### use strict; use IO::Socket; my $Unix; if ($^O =~ m/unix|linux|sunos|solaris|freebsd|aix/i) { $Unix = 1 } else { $Unix = 0 } my $sock = new IO::Socket::INET ( LocalPort => '8181', Proto => 'tcp', Listen => SOMAXCONN, ReuseAddr => $Unix, ); die "Could not create socket: $!\n" unless $sock; $| = 1; my $client = $sock->accept(); print "Message1\n"; print $client "Message1\n"; # You can release client here. if not - later it will be blocked. #close($client); # Something that wastes time + separate process creator my $cmd; if ($Unix) { $cmd = 'pause 10'; $cmd .= ' &'; } else { $cmd = 'pause'; $cmd = 'cmd.exe /c start cmd.exe /c ' . $cmd; } # This will block both Unix and Win clients if the server is on Win system($cmd); close($client); $sock->shutdown(2); close($sock);

Client:

########### use strict; use IO::Socket; my $peeraddr = shift @ARGV || 'localhost'; print 'Connecting to: ' . $peeraddr . "\n"; my $sock = new IO::Socket::INET ( PeerAddr => $peeraddr, PeerPort => '8181', Proto => 'tcp', ); die "Could not create socket: $!\n" unless $sock; $| = 1; # it will block here while (<$sock>) { print; } print "done\n"; close($sock);

Replies are listed 'Best First'.
Re: Client socket blocks if server creates subprocess
by BrowserUk (Patriarch) on Dec 06, 2010 at 10:01 UTC

    This problem arises because system invokes the system API CreateProcess() with the bInheritHandles parameters set to true. The sub-process therefore inherits all the calling processes handles, including the listening socket with the inevitable affect.

    One solution would be to side step the problem by using Win32::Process to start the sub-process ensuring that the equivalent $iflags parameter is false, thus ensuring none of the calling processes handles are inherited by the sub-process.

    If you need the sub-process to inherit some handles but not others, then it will be necessary to mark those you do not wish to be inherited using the system API SetHandleInformation().

    As is usually the case in these matters, it is Perl's flawed attempts to emulate *nix on Win that lies at the core of the problem.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks a lot! It works brilliant!
Re: Client socket blocks if server creates subprocess
by chrestomanci (Priest) on Dec 05, 2010 at 22:12 UTC

    Just a theory, but the normal rule when a process forks, is that both children inherit all open file handles, including sockets, so if your parent process forks while it holds a socket connection to your child, then your child will block if either side of that fork fails to close the socket.

    In addition, you say that you are seeing strange behaviour on windows. I to have seen perl under windows do all kinds of strange things with sockets in the past, to the extent that I would now not trust windows to have a reliable implementation.

      I have the same opinion but still can not make it working. Pre-fork with socket close also will not help:
      # Workaround for Windows (will not help): my $h = fork(); if (defined $h and $h == 0) { # child close($client); system($cmd); exit(0); }