Building network services with PHP and xinetd


Not all is web and HTTP. Sometimes we need to create a network service listening to a port. We can create a TCP server in C, Java or even PHP but there’s a really helpful daemon in Linux that helps us to do it. This daemon is xinetd. In this article we are going to create a network service with PHP and xinretd.

Now we are going to create our brand new service with xinetd and PHP. Let’s start. First we are  going to create a simple network service listening to 60321 port. Our network service will say hello. The PHP script will be very complicated:

// /home/gonzalo/tests/test1.php
echo "HELLO\n";

We want to create a network service on 60321 tcp port so we need to define this port on /etc/services. We put the following line at the end of /etc/services

// /etc/services
...
myService   60321/tcp # my hello service

And finally we create out xinetd configuration script on the folder /etc/xinet.d/ , called myService (/etc/xinetd.d/myService)

# default: on
# description: my test service

service myService
{
        socket_type             = stream
        protocol                = tcp
        wait                    = no
        user                    = gonzalo
        server                  = /usr/local/bin/php-cli
        server_args             = /home/gonzalo/tests/test1.php
        log_on_success          += DURATION
        nice                    = 10
        disable                 = no
}

Now we restart xinetd

sudo /etc/init.d/xinetd restart

And we have our network service ready:

telnet localhost 60321
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HELLO
Connection closed by foreign host.

Easy. isn’t it? But it may be not really useful. So we are going to change something in our php script to accept input. Here we cannot use POST or GET parameters (that’s not HTTP) so we need to read input from stdin. In PHP (and in other languajes too) that’s pretty straightforward.

$handle = fopen('php://stdin','r');
$input = fgets($handle);
fclose($handle);

echo "hello {$input}";

Now if we run our script from CLI it will ask for input.
So if we test our network service with a telnet.

And we have our network service ready:

telnet localhost 60321
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

we type: “gonzalo” and:

gonzalo
hello gonzalo
Connection closed by foreign host.

23 thoughts on “Building network services with PHP and xinetd

  1. Great article.

    One question, if there was no CR coming in, just a fixed length 128 characters, some non printable, what would the php code look like?

    1. You can handle it with standar PHP’s file manipulation funtions. fread instead of fgets will work if you know the lenght of the buffer.

    1. You can see the client IP with the same technique than with a http request: with $_SERVER[‘REMOTE_ADDR’]. You also must take into account that remote IP can be behind a proxy have a look to: http://kcy.me/9unu

      1. This is my code and the remote IP is always zero which is my mistake?

        $IpX = $_SERVER[‘REMOTE_ADDR’];

        $handle = fopen(‘php://stdin’,’r’);
        $input = fgets($handle);
        fclose($handle);

        //Determinar longitud
        if (strlen($input) > 10) {

        //Base de DAtos
        //$IpX = ‘000.000.000.000’;
        $hostname_localhost = “localhost”;
        $database_localhost = “database”;
        $username_localhost = “user”;
        $password_localhost = “pass”;
        $localhost = mysql_connect($hostname_localhost, $username_localhost, $password_localhost) or trigger_error(mysql_error(),E_USER_ERROR);

        //CREAMOS EL QUERY PARA INGRESAR EL REGISTRO
        $sqlQuery = “INSERT INTO hitser_gps.log (Ip,Log) VALUES (‘$IpX’,’$input’)”;

        //INSERTAMOS EL REGISTRO
        $table = mysql_query($sqlQuery, $localhost) or die(mysql_error());
        };

        thanks

      2. I use:

        $host = $_SERVER['REMOTE_HOST'] ? $_SERVER['REMOTE_HOST'] : $_SERVER['HOST'];

        In my local LAN

  2. thanks, Gonzalo
    right now i’m considering my future PHP-daemon which should catch simple flags (like 0/1/2) and do things (like mysql SELECT/UPDATE, also useradd/userdel/untar archives etc).
    thing is – there will be much of connections and the number will grow up in time.
    so what about queues in this case? in nature PHP-daemon i can make forks with background jobs, i can work with queue but in case of xinetd what it’s gonna be?
    also it is possible (and will be good?) to keep opened connection to mysql?

    1. PHP is single thread. Create threads in PHP is a nightmare (with PHP and with almost all languages) queues are good solution to this problems, and also xinet.d. I’ve got one small experiment building something similar https://github.com/gonzalo123/pxinetd (I will write a blog post about this project soon). Anyway I’m not 100% percent convinced with my own solution.

      xinet.d works fine. The problem is when you need to handle big amount of incoming connections. If you need to process 1..10 request per minute works great, but if you need to process, for example, 20 per seconds your server load will raise a lot. xinet.d will create one process per request. 20 process at the same time => server load = 20 and sysadmin will be angry (nevertheless sysadmins always are angry :). In those cases we need a dedicated server. Nowadays I’m using nodeJs or python servers when I need to do this kind of things. pxinetd is an experiment to do this with PHP and React library. Anyway it’s still single thread (aka one slow process will block the entire script). IMHO Queues are the solution. Gearman or maybe one custom queue with ZeroMQ

  3. Hola Gonzalo, muy bueno tu artículo.

    Te comento y consulto lo siguiente.

    He creado un servicio que recibe una trama ISO8583 enviada por una Terminal PosNet. El problema es que el paquete que responde mi servicio es fragmentado, pero el tamaño de la trama ISO8583 no supera el 1Kb.

    ¿Cómo podría aumentar el tamaño de los frames, packets, etc .. con el xinetd y/o php?

    Gracias.

    Saludos cordiales,

  4. Hi Gonzalo, good post.

    I have created a service that receives packages sent by a PosNet Terminal in ISO8583 format. The problem is that the package that response my service is fragmented, but the size of the ISO8583 frame does not exceed 1Kb.

    How could I increase the size of frames, packets, etc. with xinetd and / or php?

    Thank you.

    Best regards,

    1. I don’t have experience with those low level issues. At least With PHP. Maybe you need to work with binary buffers. node and python are also good choice.

      1. Hi Gonzalo,

        Thanks for taking the time to respond.

        I will try to return with php://stdout, instead exit() or die() , to prevent buffering issues.

        Thank you again!

      2. maybe php output buffering functions like ob_start? (i haven’t use them in cli. Only in web environments)

  5. Hi Gonzalo, me again, hehe

    If there was no CR coming in, and I do not know the length of the buffer. Which function should I use?

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.