为什么64位连接后Perl的Windows上的IO :: Socket会抱怨“资源不可用”?

时间:2010-02-17 21:07:03

标签: windows perl sockets fork

我在Windows下创建了一个带有Perl的服务器(ActivePerl 5.10.1 build 1006),该服务器在连接时分叉,接受一些JSON数据,并将其写入数据库。我在64个客户端连接到服务器后遇到问题,尝试fork时错误消息“资源不可用”。

在Linux下运行此代码我发现许多已解散的子进程,这是通过在父进程上添加wait()调用来解决的。然而,这并没有解决问题。在Linux下运行代码可以通过Windows中允许的64个调用。

我还启动了虚拟Windows服务器,以防它受到服务器的限制,但全新安装的Perl导致相同的64连接限制。

感谢任何想法。


use IO::Socket; 
use Net::hostent; 
use JSON;
use DBI;
use Data::Dumper;

my $port=shift || 9000;
my $clients_served = 0;

while(1){
  my $server = IO::Socket::INET->new( Proto => 'tcp', 
    LocalPort => $port, 
    Listen => 1, 
    Reuse => 1); 

  die "can't setup server" unless $server; 
  print "[Server $0 is running]\n"; 

#### 
# wait for a client to connect
# once it has, fork to a seperate thread and
# retrieve the JSON data
#### 
  while (my $client = $server->accept()) { 
    my $pid = fork();

      if ($pid != 0) {
        print ("Serving client " . $clients_served++ . "\n");
      }else{
        $client->autoflush(1); 
        my $JSONObject = JSON->new->ascii->pretty->allow_nonref();
        my $hostinfo = gethostbyaddr($client->peeraddr); 
        my $client_hostname = ($hostinfo->name || $client->peerhost);

        printf "connect from %s\n", $client_hostname;

        print " $client_hostname connected..\n";
        syswrite($client, "Reached Server\n", 2048);
        if (sysread($client, my $buffer, 2048) > 0) {

          foreach my $tasks($JSONObject->decode($buffer)){
            foreach my $task (@$tasks){
              insert_record($client_hostname, $task); #empty method, contents does not affect result
            }
          }
        }

        print " $client_hostname disconnected..\n";
        close $client; 
        exit 0;
      }
  }
  $server->close();
}

exit 0;

4 个答案:

答案 0 :(得分:2)

尝试从已完成的交易中获取僵尸进程。我可以获取您的示例代码 如果我包含更多行,请继续运行:

    use POSIX ':sys_wait_h';

    if ($pid != 0) {
        print ("Serving client " . $clients_served++ . "\n");
        1 while waitpid -1, WNOHANG > 0;

如果您可能有64个同时连接,则可能需要考虑其他事项 - 在Windows上安装SIGCHLD处理程序并不合适。

答案 1 :(得分:1)

这是一个躲闪,因为它没有直接回答你的问题,但有时删除错误的最好方法是编写更少的代码 - 为什么不让别人为你做进程管理和套接字处理 - 即{ {3}}? Net::Server个性提供与您现在写的相同的行为,但我个人会考虑Net::Server::Fork

使用Net :: Server,您的应用程序将如下所示:

use strict;
use warnings;
use base 'Net::Server::Fork';

sub process_request {
    my $self = shift;
    # Do JSON stuff here.
    # The client is attached to *STDIN and *STDOUT.
}

# You might omit this and the arguments to run() because
# Net::Server has command-line and config-file handling too.

my $port = shift || 90000;

__PACKAGE__->run(
    proto => "tcp",
    port => $port,
);

我不得不说,这真的很整洁。

答案 2 :(得分:1)

我在Windows上挖掘了perl的fork问题(ActiveState Perl 5.10)。问题是,在子进程退出后,作为线程模拟的分叉“进程”的HANDLE未关闭。并且似乎有64个线程句柄的限制。由于我没有找到如何关闭这些线程句柄,我找到了另一个解决方案,发现使用真实线程超过64限制(这来自http://www.perlmonks.org/?node_id=722374):

#! perl -slw
use strict;
use IO::Socket;
use threads;

our $ADDR ||= 'localhost:35007';

my $listener = IO::Socket::INET->new(
    LocalAddr => $ADDR,
    Listen => 5,
    Reuse   => 1,
) or die $^E;

while( my $client = $listener->accept ) {
    async {
        my($port, $iaddr) = sockaddr_in( getpeername( $client ) );
        printf "From %s:%d: %s", join('.',unpack 'C4', $iaddr ), $port, $_
            while <$client>;
        close $client;
    }->detach;
}

这确实有效,至少在Windows XP上是这样。任何警告,任何人?

答案 3 :(得分:1)

我花了很长时间来解决这个问题。

每个Windows perl程序都有64个线程限制,不支持POSIX。

在分叉子进程后,父进程需要调用waitpid($ childPID,0)来释放线程,但是这会导致父进程被阻塞,从而导致问题:如果父进程必须分叉,那么分析的重点是什么?等孩子完成?

什么不清除是父母可以在以后的任何时间发出waitpid($ childPID,0)!

当我的子线程完成最后一件事时,它创建一个文件......

open OUT, ">$$.pid";
print OUT $$;
close OUT;
exit(0);

每次父母要分叉时,它会检查.pid文件,并为每个文件发出waitpid,然后删除.pid文件。

这将不允许同时超过64个连接,但它将允许父级在子线程完成工作时继续,然后在子级完成时释放线程。

另一个值得一提的是,孩子应该做的第一件事就是关闭监听套接字(问题中的$ server),这样只有父母才能回答新客户端。