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

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

在Linux下运行这个代码,我发现很多已经失效的subprocess,这是通过在父进程上添加一个wait()调用来解决的。 但是这并没有解决问题。 在Linux下运行代码可以在Windows中执行64个调用。

如果是对服务器的限制,我也会创build一个虚拟的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; 

尝试从完成的交易收获僵尸进程。 我可以让你的示例代码继续运行,如果我包括几行:

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

如果你可能有64个同时连接,你可能不得不考虑其他的东西 – 在Windows上安装SIGCHLD处理程序是不好的。

这是一个闪避,因为它不直接回答你的问题,但有时删除错误的最好方法是编写更少的代码 – 为什么不让其他人为你做进程管理和套接字处理 – 即Net :: server ? Net :: server :: Fork个性提供了与你现在写的相同的行为,但是我个人会想到Net :: server :: PreFork 。

用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, ); 

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

我已经深入了解了Windows上Perl的分叉问题(ActiveState Perl 5.10)。 问题是,在孩子退出之后,仿造为线程的分支“进程”的句柄不能关闭。 似乎有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, listn => 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上。 任何警告,任何人?

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

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

在你分叉子之后,父母需要调用waitpid($ childPID,0)来释放线程,但是这会导致父母阻塞,导致问题:如果父母必须等待孩子完成?

什么是不清楚的是,父母可以随时发出waitpid($ childPID,0)!

当我的子线程完成它所做的最后一件事是创建一个文件…

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

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

这将不允许同时连接超过64个连接,但是当子线程完成他们的工作时,它将允许父进程继续,然后在子进程结束时释放线程。

另外值得一提的是,孩子应该做的第一件事是关闭监听套接字(问题中的$服务器),以便只有父母会回答新的客户端。