domenica 20 dicembre 2009

Abbiamo tutto quello che ti server

Tradotto dal tutorial Writing Socket Servers in PHP, la cui versione originale è consultabile qui

Realizzare Server basati sui Socket in PHP

A quale tipo di lettori è destinato
Prerequisiti
Panoramica - Cos'è un server basato sui socket?
•  Tipi di Socket
Funzioni Socket del PHP
•  Creare un Socket in PHP
•  La realizzazione di un server reale
•  Utilizzazioni pratiche
•  Sicurezza
•  Uno alla volta, per carità
Aggiunte e miglioramenti possibili
Notizie sull'autore

A quale tipo di lettori è destinato

Questa lezione è destinata al programmatore PHP interessato ad esplorare l'uso delle funzioni Socket del PHP per creare un Server Internet basata sui Socket.


Prerequisiti

La lezione utilizza:
  • la PHP Sockets library. L'estensione è abilitata a compile time usando l'opzione di configurazione --enable-sockets.
  • la versione CLI (Command Line Interface) del PHP: consente di attivare il socket server dalla linea di comando.

    A partire dal PHP 4.3.0 l'eseguibile CLI viene compilato ed installato di default (ad ogni modo si può installare esplicitamente la versione CLI specificando --enable-cli a compile time).
  • il sistema operativo Linux
Nonostante questa lezione usi Linux, la libreria sockets funziona ugualmente bene negli ambienti Windows e Unix-like.

Sotto Windows, i PHP Sockets possono essere attivati levando il commento alla linea extension=php_sockets.dll nel file di configurazione php.ini


Panoramica - Cos'è un server basato sui socket?

Un server basato sui socket è un servizio assegnato ad una porta particolare che ascolta le richieste in arrivo e fornisce delle risposte ad esse.

I server di posta elettronica (POP3, SMTP) ed i server web sono buoni esempi di server basati sui socket. Un server HTTP (web) ascolta sulla porta 80 le richieste in entrata ed invia al richiedente l'HTML ed altri file (immagini, documenti exc).

I Socket Server normalmente sono continuamente in esecuzione come un service o un daemon.


Tipi di Socket

Quando si invia l'informazione attraverso la Internet, di solito la si spezzetta in pacchetti. Questo ci consente l'invio di file di grandi dimensioni in molti pezzetti più piccoli, che verranno riassemblati successivamente una volta giunti a destinazione.

Esistono due protocolli differenti per spezzettare l'informazione in pacchetti, a seconda del tipo di informazione che si sta inviando e dei requisiti di consegna.
  • TCP (Transmission Control Protocol) – i pacchetti trasmessi sono numerati e vengono poi riassemblati nel giusto ordine dal lato del destinatario per ricostituire l'intero messaggio. TCP normalmente si appoggia su IP (Internet Protocol), perciò si usa il termine TCP/IP.

    TCP garantisce che nessun dato vada perso (se un pacchetto è perduto, verrà ritrasmesso), e quindi è l'ideale per inviare immagini, file ed altre informazioni che devono giungere a destinazione intere ed intatte (come, ad esempio, un tuo messaggio di posta elettronica).
  • UDP (User Datagram Protocol) – questo è un protocollo senza connessione. Come TCP, può essere eseguito appoggiandosi sulle funzionalità del protocollo IP. La differenza è che UDP fornisce pochi servizi per il recupero degli errori di trasmissione e che non è garantito che un particolare pacchetto sarà senz'altro ricevuto dall'altro lato, né in quale ordine i pacchetti saranno ricevuti.

    UDP è particolarmente adatto nella trasmissione di dati in modalità stream come si fa ad esempio per la musica (se perdiamo qualche informazione, non è detto che cercheremo di ottenerla daccapo, perché potrebbe darsi che il pezzo perduto sia già stato riprodotto).
In questa lezione, useremo i socket TCP per essere sicuri che tutti i dati siano ricevuti integri. Gli esempi utilizzati in questa lezione possono essere convertiti con facilità per usare i socket UDP.


Funzioni Socket del PHP


Torna su

Il PHP è ben equipaggiato per gestire i socket al livello più basso. A partire dal PHP3, il PHP ha introdotto la gestione dei socket attraverso l'uso della funzione fsockopen() and other associated functions (confronta la sezione Network del manuale PHP su http://www.php.net/network). A partire dal PHP4, la funzionalità socket del PHP è stata assai migliorata con l'introduzione dell'interfaccia a basso livello dei socket stile BSD.

Nota: le funzioni socket in PHP sono tuttora considerate sperimentali e quindi potrebbero cambiare in versioni future del PHP. Il testing mostra che sono abbastanza stabili quando le si utilizza in applicazioni ben scritte.


Creare un Socket in PHP

Creare un socket a basso livello in PHP è molto simile ad usare le funzioni socket del linguaggio C ed alla programmazione dei socket che si fa in Unix. Il manuale su http://www.php.net/sockets descrive a grandi linee le funzioni disponibili e fa anche riferimento alle Unix Socket FAQ, che sono una grande risorsa per la programmazione dei socket (anche se ci vuole un po' di tempo per leggersele tutte).

Iniziamo con un esempio semplice: un socket server che ascolta una connessione sulla porta 9000, accetta una stringa in input, e la restituisce con tutti gli spazi eliminati.

Nota veloce sui numeri delle porte. I numeri di porta sotto 1024 possono essere aperti solo se si è in possesso dei privilegi di root (o dei privilegi di amministratore in Windows NT/2000). Essi sono anche per lo più riservati per servizi rinomati (come HTTP, POP3, SMTP, FTP, ecc). Per facilità d'uso e per la sicurezza, la maggior parte dei servizi programmati dall'utente ascoltano numeri di porta sopra 1024 (il massimo numero di porta disponibile è 65535, perché IPV4 lo esprime in 16 bit).
#!/usr/local/bin/php –q

<?php
// Set time limit to indefinite execution
set_time_limit (0);

// Set the ip and port we will listen on
$address = '192.168.0.100';
$port = 9000;

// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port) or die('Could not bind to address');
// Start listening for connections
socket_listen($sock);

/* Accept incoming requests and handle them as child processes */
$client = socket_accept($sock);

// Read the input from the client &#8211; 1024 bytes
$input = socket_read($client, 1024);

// Strip all white spaces from input
$output = ereg_replace("[ \t\n\r]","",$input).chr(0);

// Display output back to client
socket_write($client, $output);

// Close the client (child) socket
socket_close($client);

// Close the master sockets
socket_close($sock);
?>

Per poter mandare in esecuzione il programma bisogna assicurarsi che la prima linea #!/usr/local/bin/php –q sia la locazione dell'eseguibile del PHP CLI (o CGI). Potrebbe essere necessario modificarne i permessi per renderlo eseguibile (chmod 755 socket_server.php – dove socket_server.php è il nome del file) e lanciarlo usando ./socket_server.php dalla linea di comando.

Ora osserviamo in dettaglio ciascuna linea:
  • #!/usr/local/bin/php –q Esegue il binario CLI del con la quiet options per evitare che faccia l'output degli headers HTTP.
  • $sock = socket_create(AF_INET, SOCK_STREAM, 0) – Crea il "master" socket. Questo socket non si occuperà di servire i client, ma piuttosto ascolterà le richieste in arrivo e genererà nuovi socket per quei client. Si comporta come il socket "ascoltatore" principale.

    Dal manuale PHP (http://www.php.net/socket_create): AF_INET è il tipo di dominio del protocollo IPV4 – usato perTCP and UDP. SOCK_STREAM fornisce stream di byte in sequenza, affidabili, basati sulla connessione. Il protocollo TCP è fondato su questo tipo di connessione.
Note: per ottenere un socket di tipo UDP, basta sostituire SOCK_STREAM con SOCK_DGRAM.
  • socket_bind($sock, $address, $port) or die('Could not bind to address') – Collega il socket con l'indirizzo IP e la porta specifici.
  • socket_listen($sock) – Ascolta sulla specifica porta le connessioni in ingresso; una volta instaurata la connessione, sarà usata per creare il socket figlio.
  • $client = socket_accept($sock) – Accetta la connessione sul master socket.
  • $input = socket_read($client, 1024) – Legge dal socket accettato, 1024 byte alla volta (oppure finché non si riceve un \r, \n or \0 – a seconda del valore del terzo parametro opzionale – vedi la nota sotto).
Note: Il terzo parametro di socket_read() può essere o PHP_BINARY_READ, che usa la funzione di sistema read() ed è affidabile per leggere dati binari (il tipo di default per PHP >= 4.1.0) oppure PHP_NORMAL_READ in cui la lettura si ferma alla ricezione di \n o \r (default in PHP <= 4.0.6)
  • $output = ereg_replace("[ \t\n\r]","",$input).chr(0) – Rimuove tutti i blank, le tabulazioni e le andate a capo usando una regular expression.
L'unico punto di interesse è chr(0), che rappresenta il carattere null. Questo viene posto in coda alla stringa che sarà inviata indietro al richiedente. Il motivo per cui si termina l'output con il carattere null è che parecchie socket API dei client usano questo carattere per notificare al client la fine della trasmissione (ciò è vero con la Macromedia Flash XMLSocket API).
  • socket_write($client, $output) - Scrive l'output per il client..
  • Infine, socket_close($client) e socket_close($sock) Chiude il client socket ed il master socket.

La realizzazione di un server reale

Ora che conosci i passi fondamentali necessari per metter su un socket ed ascoltare le richieste in arrivo, sei pronto per creare un server "adulto".

Devi notare che nel codice riportato qui sopra il programma viene lanciato una sola volta, attende una connessione in entrata e poi termina. Questo può andar bene per spiegare i passi necessari a creare un server, ma risulta inadeguato per affrontare le situazioni reali. Una volta che il tuo programma è in esecuzione e risponde alle richieste in arrivo, certo non vuoi che esso termini (dato che questo comporterebbe la necessità di farlo ripartire manualmente).

Ci serve quindi un meccanismo per eseguire il programma in modo continuo – per farlo andare in loop.

Possiamo usare un while(true) { /* do something */ } per costringere il programma a rimanere continuamente in esecuzione, finchè non lo mandiamo ad un exit statement esplicito.

Espandiamo l'esempio di prima, aggiungendo le seguenti funzionalità:
  • consentire al programma di rimanere in esecuzione indefinitamente
  • predisporre la gestione della sua terminazione
  • fornire la gestione simultanea di client multipli
#!/usr/local/bin/php –q

<?php
// Set time limit to indefinite execution
set_time_limit (0);

// Set the ip and port we will listen on
$address = '192.168.0.100';
$port = 9000;
$max_clients = 10;

// Array that will hold client information
$clients = Array();

// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port) or die('Could not bind to address');
// Start listening for connections
socket_listen($sock);

// Loop continuously
while (true) {
    
// Setup clients listen socket for reading
    
$read[0] = $sock;
    for (
$i = 0; $i < $max_clients; $i++)
    {
        if (
$client[$i]['sock']  != null)
            
$read[$i + 1] = $client[$i]['sock'] ;
    }
    
// Set up a blocking call to socket_select()
    
$ready = socket_select($read,null,null,null);
    
/* if a new connection is being made add it to the client array */
    
if (in_array($sock, $read)) {
        for (
$i = 0; $i < $max_clients; $i++)
        {
            if (
$client[$i]['sock'] == null) {
                
$client[$i]['sock'] = socket_accept($sock);
                break;
            }
            elseif (
$i == $max_clients - 1)
                print (
"too many clients")
        }
        if (--
$ready <= 0)
            continue;
    }
// end if in_array
    
    // If a client is trying to write - handle it now
    
for ($i = 0; $i < $max_clients; $i++) // for each client
    
{
        if (
in_array($client[$i]['sock'] , $read))
        {
            
$input = socket_read($client[$i]['sock'] , 1024);
            if (
$input == null) {
                
// Zero length string meaning disconnected
                
unset($client[$i]);
            }
            
$n = trim($input);
            if (
$input == 'exit') {
                
// requested disconnect
                
socket_close($client[$i]['sock']);
            } elseif (
$input) {
                
// strip white spaces and write back to user
                
$output = ereg_replace("[ \t\n\r]","",$input).chr(0);
                
socket_write($client[$i]['sock'],$output);
            }
        } else {
            
// Close the socket
            
socket_close($client[$i]['sock']);
            unset(
$client[$i]);
        }
    }
}
// end while
// Close the master sockets
socket_close($sock);
?>

La funzionalità di base è la stessa del primo esempio, con una prestazione aggiuntiva – quando un utente passa la stringa 'exit' il programma termina la connessione con l'utente.

Questo programma è molto simile al primo, fatta eccezione che all'interno del loop abbiamo quattro blocchi fondamentali di codice.

  1. Metter su i socket per la lettura.
  2. Mettersi in ascolto di nuovi client e predisporli compilando l'array $client.
  3. Ascoltare ciò che i client ci scrivono e registrarne l'input.
  4. Gestire l'input del client.
Una nuova funzione che utilizziamo è socket_select($read,null,null,null); questa invoca la system call select() sull'array di socket che le viene fornito ed attende che essi modifichino il loro stato. Questo provocherà il blocco di tutti i socket finché non si verifica un cambiamento di stato, che a questo punto verrà gestito.

Ciliegina sulla torta, potresti trovarti in una situazione in cui ti serve trasmettere qualche informazione a tutti gli altri client connessi (cose che capitano ad esempio in un ambiente di chat molti-a-molti). Lo si può ottenere con il codice seguente:
$output = 'This is my broadcast message'.chr(0);
for (
$j = 0; $j < MAX_CLIENTS; $j++) // for each client
{
    if (
$client[$j]['sock']) {
        
socket_write($client[$j]['sock'], $output);
    }
}


Utilizzazioni pratiche

Ora che conosci gli aspetti fondamentali della creazione di un socket server, l'unico limite sta nella tua immaginazione. Ecco alcune idee:
  • Server di chat (utilizzando un'interfaccia grafica o basata sul testo). Lo si può realizzare per divertimento o per scopi più seri (ad es. per gestire il supporto on line della clientela).
  • Streaming di informazione in tempo reale (notizie, quotazioni di borsa, ecc.)
  • Streaming di dati multimediali (immagini, video e musica)
  • Server di autenticazione
  • Semplici server web, POP3, SMTP ed FTP.
Inoltre la libreria socket può essere utilizzata per creare non solo server, ma anche client.


Sicurezza

Quando si realizzano programmi che saranno accessibili online, bisogna prendere in considerazione l'aspetto della sicurezza. Questo è vero per normali script PHP, così come per programmi che sono continuamente in esecuzione come i socket server.

Ci sono molti aspetti da prendere in considerazione per realizzare una politica di sicurezza completa, dalla programmazione, al controllo degli accessi e tante altre cose.

Quando si pianifica una politica di sicurezza bisogna considerare molti punti. Qui consideriamo soltanto alcuni da cui si può incominciare:
  • Accesso ai file – bisogna porre dei limiti all'accesso ai file. Se il server consente l'accesso ai file (ad es. un web server), assicurati che vengano forniti all'esterno soltanto i files contenuti in una certa cartella. Sarebbe un erroraccio rendere accessibili /etc/passwd o /etc/shadow.

    Si può limitare l'accesso ai file definendo una cartella padre o una cartella root e consentendo al server solamente l'accesso alle sottocartelle. Un'altra buona idea è quella di "disinfettare" i dati inviati dall'utente, rimuovendo da essi caratteri estranei e pericolosi (come il "../" usato per accedere file contenuti in cartelle di un livello superiore).
  • Cadere in piedi – nei casi in cui il server si ferma, dovresti organizzarti perché si fermi in maniera sicura. Cioè, se il tuo server non può più funzionare come desiderato, dovrebbe essere portato in un stato in cui non può causare danni – terminare il programma e bloccare l'esecuzione.
  • Autenticazione – per servizi sensibili, è raccomandato che si usi l'autenticazione come parte delle specifiche di comunicazione. Persino se si usa un'interfaccia personalizzata in flash o in Visual Basic, non è garantito che qualcuno non sia capace di "annusare" la connessione di rete e di decifrare il protocollo che si sta utilizzando (e tutti i dati che si trasferiscono).

    Un ottimo modo di realizzare l'autenticazione è quello di non consentire che avvenga nessuna azione finché l'utente non si è autenticato con successo (nell'esempio di prima questo può essere realizzato settando $client[$i]['authenticated'] = true dopo una autenticazione riuscita).
  • Crittografia – la crittografia è un ottimo metodo per proteggere le informazioni sensibili che passano atttraverso il server. I sistemi di crittografia possono essere utili specialmente se combinati con le procedure suggerite prima (specialmente l'autenticazione su canali criptati). Per fortuna, il PHP possiede un'eccellente libreria crittografica (la Mcrypt library – vedi http://www.php.net/mcrypt per ulteriori dettagli).

Uno alla volta, per carità

Di solito si vuole che ci sia una sola istanza del server in esecuzione in un dato momento. Nell'esempio proposto abbiamo usato socket_bind() or die('Could not bind to address'). Questo significa che se il programma prova a collegare una porta già in uso esso andrà in abort visualizzando un messaggio di errore.

Si può ottenere un controllo più sofisticato del programma usando il programma pidof che è attivabile dalla riga di comando (/sbin/pidof). Questo programma visualizza la lista dei process ID (pid) dei processi in esecuzione. Nell'usare pidof, ricordatevi di attivarlo con l'argomento -x per visualizzare anche i process id degli shell script.


Aggiunte e miglioramenti possibili

  • Aggiungere controllo dei processi e dei thread (si può fare usando PCNTL).
  • Aggiungere funzioni di controllo dei processi (di cui abbiamo parlato prima) – per essere sicuri che soltanto una singola istanza del programma è in esecuzione in un dato momento (usando /sbin/pidof –x program name).
  • Mandare in esecuzione periodicamente un script per controllare che il servizio è in esecuzione. Lo si può fare con un cron job in *nix (digita man crontab nella shell *nix per ulteriori informazioni) o sotto Windows come un task programmato (sotto Pannello di Controllo).
  • L'interfaccia di front-end può essere implementata in C++, VB, Flash (usando gli XMLSockets), Java o qualsiasi software che supporti i socket TCP/IP o UDP.
  • Se il server sarà in esecuzione senza controllo umano, potrebbe essere auspicabile la creazione di una funzione personalizzata di controllo degli errori che li memorizzi su di un file di testo o in un database invece di visualizzarli.

Notizie sull'autore

Ori Staub è un analista di sistemi senior, uno sviluppatore ed un consulente specializzato nelle soluzioni basate sul web. Ha sviluppato molte soluzioni web ed e-commerce and web per farle lavorare in stretta collaborazione con modelli di client gestionale pre-esistenti.

mercoledì 16 dicembre 2009

Catenaccio



Il bello del Marconi è che quella porta sta aperta tutto il giorno, aperta alle persone, aperta alla città, dalla mattina presto fino alle dieci di sera. Neanche i negozi, neanche le chiese, fanno la stessa cosa.

Poveretti quelli che hanno pensato di poterla chiudere con un catenaccio anche per pochi minuti. Le squadre che si chiudono in difesa, che fanno catenaccio, sono squadre deboli e prendono lo stesso un sacco di goal.

Soprattutto per loro saremo sempre aperti, perché siamo la Scuola Pubblica, la casa di tutti, la comunità in cui cresciamo insieme.

venerdì 11 dicembre 2009

I cento passi



I cento passi è un film di Marco Tullio Giordana del 2000

Prosegue con questo film straordinario la rassegna "La radio nel cinema", presso la nostra scuola alle 16.30 di oggi.

La locandina completa qui.

Vi attendiamo in tantissimi!

venerdì 4 dicembre 2009

Radiofreccia



Radiofreccia è un film di Luciano Ligabue del 1998

Prosegue con questo splendido film la rassegna "La radio nel cinema", presso la nostra scuola alle 16.30 di oggi.

La locandina completa qui.

Vi attendiamo numerosi!

martedì 1 dicembre 2009

Frettolosamente fiori

Budding Yellow Flower in Blades of Green GrassI fiori di campo
di un giallo chiassoso
veloci prepotenti invincibili
sbocciano sbocciano sbocciano

Non sarò la rosa
che tu annuserai emozionata
che punge le dita
se è gialla lo è di gelosia
e piano appassisce in un vaso

Perdona la fretta
nel sogno sognavo un aprile
sfacciato
a vederti arrossire
finché anche tu accelerando
mi avresti voluto
mi avresti veduto venire

Ed era un ulivo
complice
nel sogno era tetto e riparo
nell'incubo
vi giunsi in anticipo
frettoloso giallo amante
a disagio
da solo
di novembre
e di notte

Per me non piangete
se uno è già giallo
mai gli può capitare
di ingiallire.




Il Marconi piange per Nicola De Marzo e per il suo amico Antonio De Stefano, andati via troppo presto e troppo ingiustamente.


Non vi dimenticheremo mai.

Su Facebook e qui, tutti possono lasciare un proprio pensiero per Nicola e Antonio.

L'ultimo saluto ad Antonio avrà luogo oggi martedì 1 dicembre alle ore 16.00, nella Parrocchia SANTISSIMO ROSARIO IN S. NICOLA in via Manzoni 1 a Carbonara di Bari.

sabato 28 novembre 2009

Tabelle con stile


Applicando gli stili CSS alle tabelle HMTL si possono realizzare effetti impensati.

Segnalo un bel wizard che vi può aiutare ad ottenere quello che gradite di più.
Qui sotto un esempio.

Roma
Napoli
Perugia
Milano
Venezia
Potenza
Bari
431
262
564
880
819
130
Pescara
210
245
273
589
528
309
Firenze
284
474
150
317
274
609
Bologna
398
588
243
219
156
723
Torino
682
871
547
142
405
1006
Genova
523
713
389
141
398
848

venerdì 27 novembre 2009

Quarto potere



Quarto potere è un film di Orson Welles del 1941

Prosegue con il miglior film americano di tutti i tempi la rassegna "La radio nel cinema", presso la nostra scuola alle 16.30 di oggi.

La locandina completa qui.

Vi attendiamo numerosi!