为什么 Windows 上的 Perl IO::Socket 会抱怨“资源不可用”? 64 个连接后?
我在 Windows 下使用 Perl 创建了一个服务器(ActivePerl 5.10.1 build 1006),该服务器在连接时分叉,接受一些 JSON 数据,并将其写入数据库。在 64 个客户端连接到服务器后,我遇到了问题,尝试分叉时出现错误消息“资源不可用”。
在 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;
I created a server with Perl under Windows (ActivePerl 5.10.1 build 1006) that forks upon being connected to, accepts some JSON data, and writes it to a database. I am running into a problem after 64 clients connect to the server, the error message being "Resource is not available" when trying to fork.
Running this code under Linux I found many defunct child process, which was solved by adding a wait()
call on the parent. This however did not solve the problem. Running the code under Linux works past the 64 calls allowed in Windows.
I also spun up a virtual Windows server in case it was restrictions on the server, but a fresh install of Perl resulted in the same 64 connection limit.
Any ideas is appreciated.
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;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
尝试从已完成的事务中获取僵尸进程。我可以让你的示例代码
如果我再添加几行,请继续运行:
如果您可能有 64 个同时 连接,您可能必须考虑其他事情 - 安装
SIGCHLD
处理程序是没有好处的在 Windows 上。Try reaping the zombie processes from the finished transactions. I can get your sample code to
keep running if I include a couple more lines:
If you might have 64 simultaneous connections, you might have to think of something else -- it's no good to install a
SIGCHLD
handler on Windows.这是一种回避,因为它不能直接回答您的问题,但有时消除错误的最佳方法是编写更少的代码 - 为什么不让其他人为您进行进程管理和套接字处理 - 即 Net::Server? Net::Server::Fork 个性提供与你现在正在写,尽管我个人会考虑 Net::Server::改为 PreFork。
使用 Net::Server,您的应用程序将如下所示:
我不得不说,这确实非常整洁。
This is a dodge since it doesn't answer your question directly, but sometimes the best way to remove bugs is to write less code -- why not let someone else do the process management and socket handling for you -- namely Net::Server? The Net::Server::Fork personality offers the same behavior as you're writing now, although personally I would think about Net::Server::PreFork instead.
With Net::Server, your app would look like:
Which is really pretty tidy, I have to say.
我已经挖掘了 Windows 上 perl 分支的问题(ActiveState Perl 5.10)。问题是,子进程退出后,模拟为线程的分叉“进程”的句柄并未关闭。而且 64 个线程句柄似乎有限制。由于我没有找到如何关闭这些线程句柄,因此我寻找了另一个解决方案,发现使用真实线程可以超过 64 个限制(这来自 http://www.perlmonks.org/?node_id=722374):
这确实有效,至少在 Windows XP 上是这样。有什么注意事项吗?
I've digged the problem of perl's fork on Windows (ActiveState Perl 5.10). The problem is that after child exits, the HANDLE for the forked "process", emulated as thread, is not closed. And there seems to be a limit for 64 thread handles. Since I didn't find how to close those thread handles, I've looked for another solution and found that using real threads works past 64 limit (this comes from http://www.perlmonks.org/?node_id=722374):
This really works, at least on Windows XP. Any caveats, anybody?
我花了很长时间解决这个问题。
每个 Windows perl 程序的线程数限制为 64 个,并且不支持 POSIX。
在 fork 一个子进程后,父进程需要调用 waitpid($childPID,0) 来释放线程,但这会导致父进程阻塞,从而产生一个问题:如果父进程必须等待子进程,那么 fork 的意义何在?孩子要完成吗?
不清楚的是父进程可以在以后的任何时间发出 waitpid($childPID,0) !
当我的子线程完成最后一件事时,它所做的就是创建一个文件...
每次父线程要分叉时,它都会检查 .pid 文件,并为每个文件发出 waitpid,然后删除 .pid 文件。
这将不允许同时有超过 64 个连接,但它将允许父线程在子线程完成工作时继续,然后在子线程完成时释放线程。
另一个值得一提的是,子进程应该做的第一件事是关闭侦听套接字(问题中的 $server),以便只有父进程会应答新客户端。
I spent a long time working this out.
Each Windows perl program has a limit of 64 threads and POSIX is not supported.
After you fork a child, the parent needs to call waitpid($childPID,0) to free up the thread, however this causes the parent to block, leading to the question: What is the point of forking if the parent must wait for the child to finish?
WHAT WAS NOT CLEAR is that the parent can issue waitpid($childPID,0) at any time later on!
When my child thread has finished the last thing it does is create a file...
Each time the parent is about to fork it checks for .pid files, and issues the waitpid for each and then deletes the .pid file.
This will not allow more than 64 connections at the same time, but it will allow the parent to continue while child threads do their work, and then free up threads when the child has finished.
Another worthwile mention is that the first thing the child should do is to close the listening socket ($server in the question) so that only the parent will answer new clients.