Scriptorama.nl

Header image showing a keyboard, mouse, laptop and books on design patterns

"Multi-tasken" met een PHP/inetd socketserver

Een socket applicatie kun je schrijven in combinatie met inetd of xinetd. Je kunt de data via STDIN binnen php gebruiken. Maar wat als je ook nog elke 10 seconden iets uit wilt voeren? In dit artikel beschrijf ik hoe je dit in combinatie met inetd kunt realiseren.

Met inetd kun je op zeer gemakkelijke wijzen een socket applicatie schrijven. Ik zal kort uitleggen hoe je dat op kunt zetten.

Systeemconfiguratie

Voeg dit toe in /etc/services:

scriptorama 30000/tcp #Scriptorama test socket

Hiermee definieer je een service genaamd scriptorama, luiterend op tcp poort 30000. Zorg er uiteraard voor dat je een vrije poort gebruikt.

Vervolgens moet je in /etc/inetd.conf het volgende toevoegen:

scriptorama stream tcp nowait root /path/to/socket.php socket.php

Om dit actief te maken moet inetd herstart worden.
Eerst even kijken welk process het is, en vervolgens kill -HUP:

# ps ax|grep inet
96 ?? Ss 0:01.61 /usr/sbin/inetd -wW
# kill -HUP 96

We hebben nu een draaiende configuratie die de data via poort 30000 naar ons php script sluist. Deze data kan via de standaard input (STDIN) worden gelezen.

Script

PHP:
  1. #!/usr/local/bin/php -q
  2. <?php
  3.  
  4.     $fp = @fopen('php://stdin', 'r');
  5.     stream_set_blocking($fp, 0);
  6.     $line = '';
  7.  
  8.     //loop infinitely
  9.     while(1) {
  10.  
  11.         //read stdin
  12.         $stdin = fgets($fp);
  13.  
  14.         //if stdin is not empty, concat it to the line
  15.         if($stdin != '') {
  16.             $line .= $stdin;
  17.         }
  18.  
  19.         //if we encounter a newline parse it to datahandler and clear line
  20.         if(strstr($line, "\n")) {
  21.             echo $line; //send line back to the client
  22.             $line = '';
  23.         }
  24.  
  25.         //timer: execute ever second
  26.         $time = time();
  27.         if($time> $prevtime) {
  28.  
  29.             //execute our stuff every 10 seconds
  30.             if($delayTimer> 9) {
  31.                 echo "10 seconden voorbij";
  32.                 $delayTimer = 0;
  33.             }
  34.             $delayTimer++;
  35.             $prevtime = $time;
  36.         }
  37.  
  38.         //sleep 50 microseconds to save cpu power
  39.         usleep(50);
  40.     }
  41.  
  42. ?>

Zorg er voor dat dit script executable is door het volgende commando uit te voeren:
# chmod a+x socket.php
Zie ook dit scriptorama artikel voor meer informatie over commandline opties.

Beschrijving script

Als eerste wordt er een filepointer naar de stream php://stdin geopend. Wat vervolgens opvalt is de stream_set_blocking functie. Normaal gesproken wacht de fgets functie op een newline. Het script stopt dus als het ware totdat er data via de socket is ingevoerd. Door stream_set_blocking uit te schakelen wordt er niet gewacht op input van de client maar gaat het script gelijk verder. Met een oneindige while loop wordt de data via STDIN ingelezen en aan de string $line toegevoed. Zodra er een newline wordt gedetecteerd wordt de gecollecteerde input naar de client ge-echo'ed en $line weer leeg gemaakt.
Ook is er een timer aanwezig die elke 10 seconden de string "10 seconden voorbij" naar de client echo'ed. De usleep functie zorgt er voor dat de cpu niet 100% belast wordt door 50 milliseconde te wachten.

Ik heb ook naar het concept "ticks" binnen PHP gekeken waarbij sommigen claimen een soort multi-tasking te kunnen bewerkstelligen. Ik heb het getest, maar dat blijkt toch niet zo te zijn. De gedeclareerde functies worden wel gelijktijdig gestart, maar blijken toch op elkaar te wachten. Dit is jammer omdat je af en toe wel echte multitasking kunt gebruiken.

Lees hier meer over de stream_set_blocking functie.

Reageer ook!

Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>