Re: A suicidal parent OR death of a forking server
by ikegami (Patriarch) on Feb 09, 2010 at 16:21 UTC
|
You don't have any synchronicity problem if you don't fork. So don't fork. use IO::Select instead.
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET qw( );
use IO::Select qw( );
sub process_msg {
my ($client, $msg) = @_;
chomp $msg;
my $host = $client->peerhost;
print "$host said '$msg'\n";
return lc($msg) ne 'quit';
}
my $server = IO::Socket::INET->new(
...
) or die("Couldn't create server socket: $!\n");
my $select = IO::Select->new($server);
my %bufs;
while (my @ready = $select->can_read) {
for my $fh (@ready) {
if ($fh == $server) {
my $client = $server->accept;
$select->add($client);
$bufs{fileno($client)} = '';
my $host = $client->peerhost;
print "[Accepted connection from $host]\n";
}
else {
our $buf; local *buf = \$bufs{fileno($fh)};
my $rv = sysread($fh, $buf, 64*1024, length($buf));
if (!$rv) {
my $host = $fh->peerhost;
if (defined($rv)) {
print "[Error reading from host $host]\n";
} else {
print "[Connection from $host terminated]\n";
}
process_msg($fh, $buf) if length($buf);
delete $bufs{fileno($fh)};
$sel->remove($fh);
next;
}
while ($buf =~ s/\G(.*\n)//) {
if (!process_msg($fh, "$1")) {
my $host = $fh->peerhost;
print "[Connection from $host terminated]\n";
delete $bufs{fileno($fh)};
$sel->remove($fh);
last;
}
}
}
}
}
| [reply] [Watch: Dir/Any] [d/l] |
|
Instead of implementing a limited cooperative multitasking system using select, it's simpler to use threads or Coro.
The following is the Coro equivalent of the code in the parent post.
#!/usr/bin/perl
use strict;
use warnings;
use Coro qw( async );
use IO::Socket::INET qw( );
sub client {
my ($sock) = @_;
my $host = $client->peerhost;
print "[Accepted connection from $host]\n";
while (!eof($sock)) {
my $msg = <$sock>;
if (!defined($msg)) {
print "[Error reading from host $host]\n";
return;
}
print "$host said '$msg'\n";
last if $msg eq 'quit';
}
print "[Connection from $host terminated]\n";
}
my $server = IO::Socket::INET->new(
...
) or die("Couldn't create server socket: $!\n");
async(\&client, $server->accept) while 1;
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Instead of implementing a limited cooperative multitasking system using select, it's simpler to use ... Coro.
But what you've posted here is just as limited as the select version (that you also posted). It's not multi-tasking, Just cooperative time-sharing.
It suffers from all the same limitations and caveats as the select implementation, except that they are stuffed under the cover of a "pretty interface".
Limitations that include:
- The inability to scale across multiple cores.
- The need to break up cpu-intensive processing into "bite-sized chunks".
Yesterdays years decades solution masquerading as something new, that headlines with what is both a factual and technical lie.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [Watch: Dir/Any] |
|
|
Re: A suicidal parent OR death of a forking server
by SuicideJunkie (Vicar) on Feb 09, 2010 at 14:03 UTC
|
1) Don't have the child die. Simply have it return instead.
2)a) Keep a count and refuse any more connections.
b) Have exactly 5 children at all times, that don't return but merely go idle when their client disconnects. Have the parent check for any available idle children when a new connection comes in, and assign the connection to one of them. Refuse if no children are idle.
3)a) Use a semaphore.
b) Push onto a thread safe array of commands to do from the children, and shift off the action to do next in a work thread.
c) Token passing between the children.
d) Etc.
| [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server
by roboticus (Chancellor) on Feb 09, 2010 at 14:09 UTC
|
MonkeyMonk:
Regarding (B): Since you make the connection using the accept method on your socket, all you need to do is keep track of the number of connections you have. Once you have five connections, simply stop accepting connections until one of the children disconnect.
...roboticus
| [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server (Use threads)
by BrowserUk (Patriarch) on Feb 09, 2010 at 19:23 UTC
|
#! perl -slw
use strict;
use threads;
use threads::shared;
use IO::Socket;
use constant CRLF => chr( 13 ) . chr( 10 );
my $server = IO::Socket::INET->new(
LocalHost => 'localhost',
LocalPort => '7070',
Proto => 'tcp',
Listen => 5,
Reuse => 1,
) or die "Couldn't create listening socket";
my $running :shared = 0;
$/ = $\ = CRLF;
while( 1 ) {
my $client = $server->accept;
print "running: $running";
close( $client ), next if $running >= 5;
print "Accepted connection from $client";
async {
{ lock $running; ++$running;}
print "$client running";
while( ( my $input = <$client> ) ne 'quit' . CRLF ) {
chomp $input;
if( $input eq 'printhelp' ) {
print $client 'Some help' . CRLF;
}
elsif( $input eq 'cmd1' ) {
print $client 'CMD1 stuff' . CRLF;
}
elsif( $input eq 'cmd2' ) {
print $client 'CMD2 stuff' . CRLF;
}
elsif( $input eq 'cmd3' ) {
print $client 'CMD3 stuff' . CRLF;
}
else {
print $client "Unrecognised command; '$input'" . CRLF;
}
}
lock $running; --$running;
print "$client done";
}->detach;
}
close $server;
That really is the complete program to satisfy your specs. Though you might want to add a signal handler to clean up a little when interupted.
It easier than fork because you can use shared memory to count how many concurrent clients you have without getting into the complexities of IPC.
It's better than select because: a) you don't have to get into writing your own line handling; b) if one of those commands takes 1/2 hour to complete, the whole server doesn't grind to a halt.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [Watch: Dir/Any] [d/l] |
|
That is the cleanest example IO::Socket::INET server I have seen yet. Nicely done.
| [reply] [Watch: Dir/Any] |
|
Thanks. It does have limitations thought. Specifically, it will only handle low 10s of concurrent connections before memory becomes an issue. And even if you keep within those levels of concurrency, if the connections are short-lived and made at high speed, it will be CPU-expensive.
But for the OPs low concurrency, long-lived connections application, it is remarkably simple and stable.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [Watch: Dir/Any] |
|
|
|
|
| [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server
by cdarke (Prior) on Feb 09, 2010 at 15:05 UTC
|
If it is indeed homework then I expect the tutor wanted you to set the Listen parameter to 5 (5 is a magic number with listen). However it only sets the size of the connecting queue, it does not include clients already connected. | [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server
by MidLifeXis (Monsignor) on Feb 09, 2010 at 14:33 UTC
|
This sounds very much like a homework assignment. Not saying it is, but if so, please say so, as the types of answers you will get may be different (directed toward having you think through the answer, rather than giving the answer directly, for example).
However, nice job on explaining your problem and providing the information about what you have already tried.
It is said that "only perl can parse Perl." I don't even come close until my 3rd cup of coffee. --MidLifeXis
| [reply] [Watch: Dir/Any] |
|
Please see my comments to the original post.
Thanks!
| [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server
by MonkeyMonk (Sexton) on Feb 10, 2010 at 09:44 UTC
|
@MidLifeXis:
Definitely not a homework assignment. I can understand the reasons for the question though.
The above code is just one side of the program. The other side has a RS232 serial interface( and that is why I mentioned "enable queuing of commands" ).
Apologies for not mentioning this earlier : my intentions were never to mislead the monks! I thought it is best to tackle one issue at a time instead of going into the details.
I do not know how others dealt with things when facing forking and threads. Personally I have been using Perl for quite some time now and can understand it to a fair degree. _However_, when dealing with forks/threads, it requires a "fundamental" shift in the way we think. "Fundamental" might be apt when I look at it from this of the fence. When I reach the other side, I might see it differently. But until then, it is still fundamental for me :)
@BrowserUk (Apostle) :
When dealing with RS232 interfaces, there is indeed a problem of "waiting" and then there is this perennial issue of ensuring that the interface is alive. I will have to look into the various scenarios and see how I can implement queuing.
Many thanks everyone. I still haven't opened my dictionary to check the meanings of the words here.
I think I will be occupied for quite some time just going through the replies and understanding what is being said!
| [reply] [Watch: Dir/Any] |
Re: A suicidal parent OR death of a forking server
by MonkeyMonk (Sexton) on Feb 10, 2010 at 09:49 UTC
|
| [reply] [Watch: Dir/Any] |