Questa pillola è dedicata a tutti quelli che hanno le manine lunghe come me e nelle domeniche pomeriggio invece di lavorare passano tempo a sperimentare
Era da tempo che ero curioso di capire come di preciso TCPServer e XINETD funzionassero!
Premetto che già un po conoscevo il funzionamento base di questi 2 applicativi per linux xo se non si prova...non si può essere certi...
Qui farò le prove solo con TCPServer, comunque, questi due hanno un funzionamento praticamente identico al max cambiano le variabili di ambiente
---===[ COSA SONO ]===---
TCPServer e XINETD sono 2 utilissimi utility, se cosi le vogliamo chiamare, che consentono di sviluppare potenti applicativi di rete in maniera semplice e veloce!
TCPServer e XINETD infatti sono un tramite tra il vostro applicativo ed il client che si connette!
---===[ COME FUNZIONANO ]===---
La mia parte ascii non ha resistito...^^
codice:
INTERNET
----------> Macchina Linux ----> TCPServer\XINETD ----> Programma richiesto
ovvero arriva una richiesta di connessione da internet al vostro pc. Mettiamo che abbiate detto a TCPServer che deve avviare il programma chiamato XYZ quando c'è una richiesta di connessione all'indirizzo IP 0.0.0.0 (quindi tutti quelli disponibili) e alla porta 1234.
Quando vi connetterete a quella porta e all'ip della vostra macchina all'istante TCPServer avvierà il programma che gli avete indicato. Prima di avviare il programma imposterà alcune variabili di ambiente. Stesso tipo di operazione la fa ovviamente anche XINETD. A questo punto comunicherete con le due utility tramite lo standard input e lo standard output!
Ogni cosa che il client vi invierà sarà disponibile sullo standard input ed ogni che vorrete inviare dovrà essere inviata allo standard output.
Per chiarirvi un po l'idea su cosa sono lo standard input e lo standard output date 1 occhio a:
http://www.phpfreaks.com/tutorials/86/0.php
http://www.phpbuilder.com/columns/darrell20000319.php3
Trattano come usare PHP come scripting da shell, ovviamente sono in inglese. Se non ricordo male ci dovrebbe anche essere 1 articolo su http://freephp.html.it
Quindi tornando al discorso di prima ci sarà 1 situazione di questo tipo
Client <---> Utility <---> Vostro Programma
spero di essere stato chiaro ^^
Purtroppo questi sono concetti di base quindi è bene che ve li studiate prima di continuare a leggere ^^
---===[ COME USARLI ]===---
Ho fatto un piccolo script nel quale ogni volta che viene battuto invio sul client lui invia il testo inviato con una stringa davanti tipo "### Echo: "
Lo script è veramente banale, i commenti lo spiegano da solo, comunque qui di seguito lo spezzato...e ogni spezzone è spiegato
codice:
#!/usr/bin/php -q
dico al sistema linux (o comunque posix\bsd) che questo file deve essere interpretato con l'interprete da console (ovvero l'interprete CLI) di php
mettendo in un file eseguibile #! il sistema linux capisce che quel file va "interpretato" da un programma apposito quindi avvia il programma e gli passa, tramite lo standard input tutto il resto del file
codice:
<?php
// Definisco la configurazione
define('MAX_BUFFER', 100);
// Definisco le costanti per lo standard INPUT e lo standard OUTPUT
define('STDIN', 'php//stdin');
define('STDOUT', 'php//stdout');
// Definisco alcune stringhe standard
define('MSGS_BEGIN', '### Server ECHO sviluppato in PHP ' . phpversion());
define('MSGS_BEGIN2', '### Connessione proveniente da: %s:%s');
define('MSGS_ECHO', '### Echo: %s');
in questa sezione definisco alcune costanti come la configurazione, alcuni messaggi standard e lo standard input e standard output (in seguito chiamati STDIN e STDOUT)
Come si vede il secondo e terzo messaggio contengono delle %s perche utilizzo sprintf per riempire quei dati
Una cosa molto importante da notare è che le due costanti STDIN e STDOUT non contengono una stringa ma bensi una risorsa! PHP ha riconosciuto che php://stdin e php://stdout non erano 2 stringhe qualsiasi ma bensi lo standard input e lo standard output. Queste due risorse sono tranquillamente utilizzabili con le funzioni per la gestione dei file
codice:
// Invio una stringa di riconoscimento
fwrite($fp['output'], MSGS_BEGIN . "\r\n");
fwrite($fp['output'], sprintf(MSGS_BEGIN2, $_ENV['TCPREMOTEIP'], $_ENV['TCPREMOTEPORT']) . "\r\n");
Invia i due messaggi di benvenuto al client inviando le stringe allo standard output. I messaggi sono definite nelle costanti di su.
Le due variabili $_ENV['TCPREMOTEIP'] e $_ENV['TCPREMOTEPORT'] le definisce TCPServer al momento della connessione
codice:
// Inizializzo il buffer
$buffer = '';
// Raccolgo i dati
while($data = fread($fp['input'], 1)) {
Inizializzo il buffer, ovvero imposto la variabile, e leggo un carattere per volta dallo stdin. Questo per evitare problemi, infatti non essendo una socket, ma un file non l'ho impostato sulla modalità non blocking.
Se avessi usato gli stream per gestire il tutto avrei potuto impostare la modalità non blocking e quindi leggere blocchi di dato maggiori. Se avessi messo 10 al posto di uno finche sullo standard input non ci sarebberò stati 10 byte a me non sarebbe ritornato nulla. Quindi, anche premendo invio, non sarebbe successo niente.
Invece in questo modo ogni volta che arriva un carattare viene processato.
codice:
// Controllo se il byte letto corrisponde all'invio
if ($data != "\n") {
// Se non corrisponde controlla la dimensione del buffer
if (strlen($buffer) > MAX_BUFFER) {
// Se la dimensione massima del buffer e' superata resetta
// il buffer con il dato appena letto
$buffer = $data;
} else {
// Se la dimensione massima non e' stata superata aggiunge
// il dato al buffer attuale
$buffer .= $data;
}
A questo punto devo processare il byte ricevuto, quindi controllo se è un carattere di invio, ovvero se contiene uno \n o meno.
Se non contiene lo \n, quindi l'andata a capo processo se la stringa ha superato la lunghezza massima del buffer con un semplice if. Se il buffer è stato riempito tutto lo svuoto e lo riempo con il nuovo carattere, alrtimenti, se c'è ancora spazio, aggiungo il byte appena letto.
codice:
} else {
// E' arrivato il momento di replicare i dati
// quindi stampiamo il buffer e poi lo svuotiamo
fwrite($fp['output'], sprintf(MSGS_ECHO, $buffer) . "\r\n");
// Svuotiamo il buffer
$buffer = '';
}
}
// --- Esecuzione terminata ---
?>
Se invece al programma arriva l'invio invia alla risorsa dello standard output la stringa che contiene il messaggio ripetuto e svuota il buffer. Come vedete anche qui uso sprintf per impostare il dato dentro la stringa
Il programma poi termina e li potete fare quello che volete, tcpserver non termina il processo fin quando non è il processo stesso a morire quindi potete fare quello di cui avete bisogno
Qui c'è il codice per intero
codice:
#!/usr/bin/php -q
<?php
// Definisco la configurazione
define('MAX_BUFFER', 100);
// Definisco le costanti per lo standard INPUT e lo standard OUTPUT
define('STDIN', 'php//stdin');
define('STDOUT', 'php//stdout');
// Definisco alcune stringhe standard
define('MSGS_BEGIN', '### Server ECHO sviluppato in PHP ' . phpversion());
define('MSGS_BEGIN2', '### Connessione proveniente da: %s:%s');
define('MSGS_ECHO', '### Echo: %s');
// Invio una stringa di riconoscimento
fwrite($fp['output'], MSGS_BEGIN . "\r\n");
fwrite($fp['output'], sprintf(MSGS_BEGIN2, $_ENV['TCPREMOTEIP'], $_ENV['TCPREMOTEPORT']) . "\r\n");
// Inizializzo il buffer
$buffer = '';
// Raccolgo i dati
while($data = fread($fp['input'], 1)) {
// Controllo se il byte letto corrisponde all'invio
if ($data != "\n") {
// Se non corrisponde controlla la dimensione del buffer
if (strlen($buffer) > MAX_BUFFER) {
// Se la dimensione massima del buffer e' superata resetta
// il buffer con il dato appena letto
$buffer = $data;
} else {
// Se la dimensione massima non e' stata superata aggiunge
// il dato al buffer attuale
$buffer .= $data;
}
} else {
// E' arrivato il momento di replicare i dati
// quindi stampiamo il buffer e poi lo svuotiamo
fwrite($fp['output'], sprintf(MSGS_ECHO, $buffer) . "\r\n");
// Svuotiamo il buffer
$buffer = '';
}
}
// --- Esecuzione terminata ---
?>
---===[ RIASSUMENDO ]===---
Utilizzando le funzioni per la gestione dei file in accoppiata allo stdin e stdout e questi applicativi è possibile scrivere svariati tipi di server! Ovviamente se avete bisogno dell'intercomunicazione tra i processo non è tra le migliore scelte ma comunque è realizzabile
---===[ COSA REALMENTE SI PUO' FARE ]===---
Questo dipende dalla vostra fantsia potete fare un server di posta...un server ftp...un server web...qualsiasi cosa stuzzichi la vostra fantasia!
Se invece volete fare cose + complesse potete anche fare 1 server centralizzato...ovvero vi spiego subito...
mettiamo che dobbiate fare l'intercomunicazione tra i processi che spuntano...per farlo è veramente semplice! Dovete creare un piccolissimo server che lavora su socket unix...e gestisce le connessioni li su...e poi i vostri client quando partono si connettono a quella socket...oppure potete usare la memoria condivisa...potete usare le pipe...insomma qualsiasi cosa vogliate come vi ho detto...dipene dalla vostra fantasia
---===[ RIFERIMENTI ]===---
Il pacchetto che contiene TCPServer si chiama uscpi-tcp ed è usatissimo con QMAIL, il più sicuro server di posta elettronica che esiste, oltre che il più potente
XINETD è standard in qualsiasi distribuzione, sostituisce il vecchio INETD, che eseguiva lo stesso dipo di operazioni.
Per aggiungere il vostro server a XINETD basta che creiate un file dentro la dir /etc/xinetd.d e li dentro aggiungiate una configurazione. Per maggiori info lanciate il comando:
man xinetd.conf
Vi mostrerà come scrivere un file di configurazione per xinetd per lanciare i vostri programmi
TCPServer invece non ha un man, per avere maggiori info guardate qui
http://cr.yp.to/ucspi-tcp/tcpserver.html
Per scaricare i vari sorgenti
http://cr.yp.to/ucspi-tcp.html
Per avere altre info sulle variabili di ambiente...
http://cr.yp.to/ucspi-tcp/environment.html
---
qui finisce questa mini pillola...a breve metto online il codice dello script su file ^^
PS: se notate Orrori...avvertite ... idem se trovate qualche idiozia ^^