这可能不是Perl特有的,但我的演示是在Perl中。Perl IO :: Socket/IO :: Select - 从“准备读取”套接字读取
我的主程序打开一个监听套接字,然后分叉一个子进程。孩子的第一份工作就是与主人联系并说出HELLO。然后它继续初始化,当它准备好时,它发送READY给主设备。
大师在分出孩子之后,等待HELLO然后进行其他初始化(主要是为其他孩子分配)。一旦它分叉了所有的孩子,并从每个孩子那里听到HELLO,它就会等待所有孩子说出就绪。
它使用IO :: Select-> can_read,然后使用$ socket-> getline来检索消息。
总之,父母未能收到READY,即使它是由孩子发送的。
这里是我的程序的匆匆简化版本,演示的错误(我试图消除不相关的问题,但有一些可能会保留)。对于是否保留消息边界以及是否需要“\ n”以及从套接字读取哪种方法,我仍然感到困惑。我真的不想考虑收集消息片段,并且我希望IO :: Select能够让我省却这些。
该演示仅生成一个孩子,为了简单起见。
#!/usr/bin/env perl
use warnings;
use strict;
use Carp;
use File::Basename;
use IO::Socket;
use IO::Select;
use IO::File; # for CONSTANTS
use Net::hostent; # for OO version of gethostbyaddr
use File::Spec qw{rel2abs}; # for getting path to this script
use POSIX qw{WNOHANG setsid}; # for daemonizing
use 5.010;
my $program = basename $0;
my $progpath = File::Spec->rel2abs(__FILE__);
my $progdir = dirname $progpath;
$| = 1; # flush STDOUT buffer regularly
# Set up a child-reaping subroutine for SIGCHLD. Prevent zombies.
#
say "setting up sigchld";
$SIG{CHLD} = sub {
local ($!, $^E, [email protected]);
while ((my $kid = waitpid(-1, WNOHANG)) > 0) {
say "Reaping child process $kid";
}
};
# Open a port for incoming connections
#
my $listen_socket = IO::Socket::INET->new(
Proto => 'tcp',
LocalPort => 2000,
Listen => SOMAXCONN,
Reuse => 1
);
croak "Can't set up listening socket: $!\n" unless $listen_socket;
my $readers = IO::Select->new($listen_socket)
or croak "Can't create the IO::Select read object";
say "Forking";
my $manager_pid;
if (!defined($manager_pid = fork)) {
exit;
}
elsif (0 == $manager_pid) {
#
# ------------------ BEGIN CHILD CODE HERE -------------------
say "Child starting";
my ($master_addr, $master_port) = split /:/, 'localhost:2000';
my $master_socket = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => $master_addr,
PeerPort => $master_port,
) or die "Cannot connect to $master_addr:$master_port";
say "Child sending HELLO.";
$master_socket->printflush("HELLO\n");
# Simulate elapsed time spent initializing...
#
say "Child sleeping for 1 second, pretending to be initializing ";
sleep 1;
#
# Finished initializing.
say "Child sending READY.";
$master_socket->printflush("READY\n");
say "Child sleeping indefinitely now.";
sleep;
exit;
# ------------------- END CHILD CODE HERE --------------------
}
# Resume parent code
# The following blocks until we get a connect() from the manager
say "Parent blocking on ready readers";
my @ready = $readers->can_read;
my $handle;
for $handle (@ready) {
if ($handle eq $listen_socket) { #connect request?
my $manager_socket = $listen_socket->accept();
say "Parent accepting connection.";
# The first message from the manager must be his greeting
#
my $greeting = $manager_socket->getline;
chomp $greeting;
say "Parent received $greeting";
}
else {
say($$, "This has to be a bug");
}
}
say "Parent will now wait until child sends a READY message.";
say "NOTE: if the bug works, Ill never receive the message!!";
################################################################################
#
# Wait until all managers have sent a 'READY' message to indicate they've
# finished initializing.
#
################################################################################
$readers->add($handle); # add the newly-established socket to the child
do {
@ready = $readers->can_read;
say "Parent is ignoring a signal." if [email protected];
} until @ready;
# a lot of overkill for demo
for my $socket (@ready) {
if ($socket ne $listen_socket) {
my $user_input;
$user_input = $socket->getline;
my $bytes = length $user_input;
if ($bytes > 0) {
chomp $user_input;
if ($user_input eq 'READY') {
say "Parent got $user_input!";
$readers->remove($socket);
}
else {
say($$, "$program RECVS $user_input??");
}
}
else {
say($$, "$program RECVs zero length message? EOF?");
$readers->remove($socket);
}
}
else {
say($$, "$program RECVS a connect on the listen socket??");
}
} # end for @ready
say "Parent is ready to sleep now.";
始终使用用'select'选择'sysread'。从未像“getline”那样使用缓冲IO。 'getline'双重没有意义,因为它是一个阻塞呼叫。 – ikegami
但选择完成后,它不会......会吗? – Chap