Perl forked socket server, stops accepting connect

2019-05-22 08:22发布

When using the following, but also when using similar code with IO::Socket::INET, I have problems with accepting new connections, once a client has disconnected.

It seems the parent stops forking new children, until all previous children have ended/disconnected. The connection is accepted though.

Does anyone have an idea what I'm doing wrong.

#!/usr/bin/perl -w
use Socket;
use POSIX qw(:sys_wait_h);

sub REAPER {
    1 until (-1 == waitpid(-1, WNOHANG));
    $SIG{CHLD} = \&REAPER;
}

$SIG{CHLD} = \&REAPER;

$server_port=1977;

socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1);
$my_addr = sockaddr_in($server_port, INADDR_ANY);
bind(SERVER, $my_addr)
    or die "Couldn't bind to port $server_port : $!\n";
listen(SERVER, SOMAXCONN)
    or die "Couldn't listen on port $server_port : $!\n";
print("[$$] STARTED\n");
while (accept(CLIENT, SERVER)) 
{
    next if $pid = fork;
        die "fork: $!" unless defined $pid;
    close(SERVER);
    print("[$$] CONNECTED\n");
    while(<CLIENT>)
    {
       print("[$$] $_\n");
    }
    print("[$$] EXIT\n");
    exit;
} 
continue 
{
    close(CLIENT);
}
print("[$$] ENDED\n");

4条回答
狗以群分
2楼-- · 2019-05-22 08:41

The problem is that accept is being interrupted by the SIGCHLD. Fix:

for (;;) {
    if (!accept(CLIENT, SERVER)) {
       next if $!{EINTR};
       die $!;
    }

    ...fork and stuff...
}
查看更多
Ridiculous、
3楼-- · 2019-05-22 08:49

On POSIX.1-2001 compliant systems, simply setting $SIG{CHLD} = 'IGNORE' should solve the problem.

查看更多
虎瘦雄心在
4楼-- · 2019-05-22 08:49

OK, I realise this is three months after the fact but I've been having the same problem trying to find an example that 1. Allows multiple simultaneous connections and it's persistent--it doesn't go away even after all the clients have quit.

I only changed the REAPER function and modernised it a little.

Here's the whole thing:

#!/usr/bin/perl -w
use Socket;
use POSIX qw(:sys_wait_h);

sub reaper {
    1 until (-1 == waitpid(-1, WNOHANG));
    return($SIG{CHLD});
}

$SIG{CHLD} = reaper();

$server_port=1977;

socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1);
$my_addr = sockaddr_in($server_port, INADDR_ANY);
bind(SERVER, $my_addr)
    or die "Couldn't bind to port $server_port : $!\n";
listen(SERVER, SOMAXCONN)
    or die "Couldn't listen on port $server_port : $!\n";
print("[$$] STARTED\n");
while (accept(CLIENT, SERVER)) 
{
    next if $pid = fork;
        die "fork: $!" unless defined $pid;
    close(SERVER);
    print("[$$] CONNECTED\n");
    while(<CLIENT>)
    {
       print("[$$] $_\n");
    }
    print("[$$] EXIT\n");
    exit;
} 
continue 
{
    close(CLIENT);
}
print("[$$] ENDED\n");
查看更多
太酷不给撩
5楼-- · 2019-05-22 08:57

I think your problem is in the REAPER - it will loop until all children have exited, since you're waiting until waitpid returns -1.

You probably want instead:

my $kid;
do {
    $kid = waitpid(-1, WNOHANG);
} while $kid > 0;

See: http://perldoc.perl.org/functions/waitpid.html

查看更多
登录 后发表回答