Back to Home

Multithreaded server in Perl? Easy!

server · daemon · server · daemon · perl · Net :: Server

Multithreaded server in Perl? Easy!

    Sometimes it becomes necessary to write some simple TCP or UDP server. For example, now my production of my own DHCP implementation is working quite successfully in production (existing ones are not suitable because of the specifics). Usually this is done simply - one cycle, listen to the socket - and voila! But the “stupid” approach is not always justified (something more complicated is needed - working in several streams, for example), and using heavy artillery is too expensive.

    Interesting? Welcome to cat.

    Preparation, or what we need


    First of all, of course, Perl itself is needed. Any more or less modern version will do. I’ll say right away how to do this under Windows, I don’t know, I didn’t check the code, but it should work.
    As it turned out, CPAN already has a ready-made Net :: Server module for such tasks . Download and install it using the tools of your package manager (libnet-server-perl in Debian / Ubuntu) or directly cpan (for an amateur):
    cpan Net::Server

    Theory


    Net :: Server provides several models for working with multi-threading: Single (one thread), Fork (a new child process for each connection), PreFork (similar, but smarter), INET (for use with inetd), MultiType (choosing one of the models - from the configuration or automatically) and so on. I personally like PreFork (quite simple and stable, but effective), and I will use it. In reality, it is better to use MultiType - it can fallback to another model if the one we need is not available.
    Everything that this package provides (module, class, as you want) is the base. You need to inherit it, redefine several functions (okay, okay, at least one!), Give it a config, and then run it. In fact, all we need is a base class. You need to add just a little bit. The basic version described in synopsis (which, however, is not without some Perl magic - for example, inheritance without declaring an inheritor class), looks like this:
    #!/usr/bin/perl
    use Net::Server::PreFork;
    @ISA = qw(Net::Server::PreFork);
    sub process_request {
       #...code...
    }
    __PACKAGE__->run();

    Works. Simply. Strong. Effectively. And nifiga is not clear (which, in general, is also a plus - you can show it to someone). In practice, we will bring it all into a more human form.

    Practice


    For a program, it is better to select a folder somewhere. We will have 3 files (package, configuration and the executable itself). Without further ado, the code:
    lib / Net / Server / Hello.pm
    #!/usr/bin/perl
    package Net::Server::Hello; # Объявляем свой пакет
    use strict; # Так в книжке написано ;)
    use warnings;
    use base qw(Net::Server::PreFork); # Наследуем
    sub process_request {   # Собственно, здесь и выполняется вся работа с запросом
       my $self = shift;    # Получаем ссылку на себя. Это Perl ООП-магия
       while () {    # Net::Server дает нам сокет как STDIN + STDOUT!
          print "Hello!\n"; # Отвечаем. Просто отвечаем на любой запрос клиента.
       }
    }
    1; # Perl-магия: пакет должен иметь return

    etc / myserver.cfg
    # Формат - ключ значение
    port 9999
    proto tcp


    bin / myserver.pl
    #!/usr/bin/perl
    use strict;
    use warnings;
    use FindBin;
    use lib "$FindBin::Bin/../lib"; # Хороший тон: держим исполняемый файл в bin,
                                    # а библиотеки (пакеты) - в lib, и т.д.
    use Net::Server::Hello;
    # Создаем экземпляр своего сервера; параметры, кстати, можно задавать в командной строке
    our $server = Net::Server::Hello->new(conf_file => "$FindBin::Bin/../etc/myserver.cfg"); 
    $server->run(); # Поехали!

    If you now start it all, connect to it with telnet and ask something, you get something like the following:
    $ telnet 127.0.0.1 9999
    Trying 127.0.0.1...
    Connected to 127.0.0.1.
    Escape character is '^]'.
    Привет!
    Hello!
    Hi!
    Hello!
    Терморектальный криптоанализ
    Hello!
    ^]quit

    telnet> quit
    Connection closed.


    What's next?


    Using the Net :: Server is not limited to writing unpretentious network «Hello world» or writing articles on the Habre . For example, I already have a working prototype of a RADIUS server, which I am going to use for my own VPN service (Kebrum, hello!). Also, there are tons of code on CPAN that Net :: Server uses in one way or another - for example, an HTTP server. Read the documentation - it steers. And may the force come with you.

    Picture from xkcd .
    If anything, this is my first habratopik, so please rotten eggs to throw right at me at habrahpost.

    Read Next