carric has asked for the wisdom of the Perl Monks concerning the following question:

So, while I'm asking questions... I have another project where I am working in a multi-threaded daemon. I have played around with Net::Daemon, Net::Server, IO::Socket, etc., and I'm becoming overwhelmed by the options, and experiencing analysis paralysis, and I'm hoping someone who has actually done some network programming could offer some advice.

So, what I want is a multi-threaded server that allows me to specify an output log file, and a listening port. The client will be netcat that connects to the server, and then I want to be able to echo commands to the netcat client (based on a list of commands I am putting in a seperate text file, so it could be changed at any time). I want to be able to capture all the output from the client to the configured log file (e.g. I send the command "ipconfig" or "set", I can send the output to a log file). It would need to append this file in case the daemon pukes and needs to be restarted (I dont want to clobber all my data).

Given that I want to send some things to a netcat listener, that has presented me with an unexpected challenge. My total lack of OOP experience is making all the modules for daemons and servers hard to understand. I know there are a numerous ways to do this, but what is best? IO::Socket and IO::Select? Net::Server? I can only find VERY limited examples of using these modules (like ONE for Net::Daemon that people have copied repeatedly from the Perl Cookbook). If I had some more examples to work from, it would help.

I'm sorry I didn't post any code, but I already feel stupid enough for not being able to grasp the OOP aspects of Perl, and I have made several tries (one of which seems to work, sort of, but for some reason won't print messages to STDOUT or any log files..) Net::Server seems most promising, but I'm still trying to figure out how to get it to use a config working from the docs.

Thank you again.

Replies are listed 'Best First'.
Re: Multi-threaded daemon
by tirwhan (Abbot) on Jan 05, 2006 at 09:37 UTC

    Are you really looking for a multithreaded daemon (i.e. one that uses Perl threads) or a multi-process daemon? Here's a (very simple) example that does what you describe with Net::Server::PreFork:

    #!/usr/bin/perl package MyEchoServer; use strict; use warnings; use Net::Server::PreFork; use base("Net::Server::PreFork"); my $self = bless { server => { port => [ 1025 ] }, logfile => "/tmp/clog", },"MyEchoServer"; open(my $clog,">>",$self->{logfile}) or die "Can't open $self->{logfil +e} : $!"; my %accept = ( ipconfig => "10.10.0.0", set => sub { $self->{value}->{$_[0]}=$_[1] }, get => sub { print "$_[0] is set to $self->{valu +e}->{$_[0]}\n" }, ); $self->run(); sub process_request { my $self = shift; print "> "; while (<STDIN>) { chomp; my ($command,@param) = split; if (exists($accept{$command})) { print {$clog} "$command ".join(" ",@param)."\n" or die "Can't print to $self->{logfile}"; if (ref($accept{$command}) eq "CODE") { $accept{$command}->(@param); } else { print $accept{$command}."\n"; } } else { print "Don't know command $command\n"; } print "> "; } }

    This is the interaction with the server (using socat instead of netcat):

    sh-2.05b$ socat readline tcp4:localhost:1025 > unknown Don't know command unknown > ipconfig 10.10.0.0 > set var1 one > get var1 var1 is set to one

    And this is the STDERR of the server and content of /tmp/clog:

    2006/01/05-10:11:06 MyEchoServer (type Net::Server::PreFork) starting! + pid(23443) Binding to TCP port 1025 on host * Group Not Defined. Defaulting to EGID '104 111 104 29' User Not Defined. Defaulting to EUID '1000' 2006/01/05-10:11:24 Server closing! sh-2.05b$ cat /tmp/clog ipconfig set value one get value

    A couple of caveats with this code:

    • Obviously this will only log commands the server recognizes.
    • The processes do not share memory, this means that a value set in one connection (via the set command) will not exist in any of the other forked children. You need to implement some sort of shared memory to do that (either use threads with shared variables or use an IPC mechanism or a database etc.).
    • All children inherit the log file handle and they'll all print to it but the output may be interleaved, you may want to look at a more robust logging mechanism (such as Log::Log4perl with Log::Log4perl::Appender::Synchronized).
    Hope this helps.

    A computer is a state machine. Threads are for people who can't program state machines. -- Alan Cox