Volevo fare un regalo di natale
Prima di iniziare leggetevi però gli articoli di fabio sulle socket altrimenti potrebbe mancarvi qualcosa :P
http://freephp.html.it/articoli/view_articolo.asp?id=76
http://freephp.html.it/articoli/view...olo.asp?id=112
---
Cosa sono le socket?
Sono quel sistema di trasmissione dei dati usato ad esempio nelle reti o su internet, tramite di esse potete contattare un'altro pc e inviargli o ricevere dei dati che poi possono essere analizzati.
Le socket sono un mondo immenso dove non finirete MAI di immaginare
Potete fare le cose più strane e divertenti, e sopratutto potete sviluppare POTENTI server scritti in PHP! Certo non potranno essere usati per reggere carichi eccessivi, ma vi voglio fare un'esempio:
Immaginate che nella ditta in cui lavorate si stia sviluppando un protocollo di comunicazione compresso, e che essendo in svuluppo deve essere testato per vedere l'efficenza, il consumo di banda, il consumo di processore e altro ancora, immaginate che per farlo avete poco tempo, quindi non potete ad esempio usare come linguaggio il C o il C++ perché entrerebberò in ballo troppi fattori esterni e quindi lo sviluppo del protocollo sarebbe estremamente lento. La soluzione ideale sarebbe un linguaggio di script, perl? python? ma se sapete programmare con php, perché dover usre un'altro linguaggio? ebbene con php è possibile sviluppare i vostri server e sviluppare codice di un certo tipo!
Per quanto riguarda le socket ci occuperemo soltanto delle socket del modulo esterno del php e non del sistema integrato delle socket in php, perché è molto più lento del modulo.
Per chi programma in C\C++ ed ha già usato le socket sotto i sistemi *nix noterà che sono praticamente uguali
Allora, facciamo un'esempio stupido, mettiamo che dovete scaricare un file dal web. Per farlo ci sono 3 modi, uno tramite le funzioni file di php, uno tramite le funzioni interne per le socket di php (che non tratteremo) ed infine tramite le socket del modulo.
Con il primo metodo molto semplicemente
andiamo analizzando questo codice:codice:<?php $fp = fopen('test.html', 'wb+t'); fwrite($fp, implode('', file('http://forum.html.it')); fclose($fp); ?>
- La prima riga si occupa di aprire il file test.html, e di crearlo se non esiste, in modalità di scrittura in modalità binaria con il supporto per la conversione del carattere di fine linea
- La seconda linea si occupa di scrivere il file che abbiamo scaricato dal web (ne riparliamo nello specifico in seguito)
- La terza chiude il file
ok, smontiamo la seconda linea nei vari comandi:
* file: questo comando supporta i cosidetti stream, come le funzioni interne di php. Questo vuol dire che posso mettere http, ftp e gli altri stream supportati internamente da php e in automatico mi restituirà il contenuto apposito. Ovvero a livello pratico oltre ad a leggere i file in locale posso leggere i file in remoto. Restituisce un'array dove per ogni elemento vi sta una linea, quindi molto comodo se dovete analizzare linea per linea, ma nel nostro caso a noi ci interessa "implodere" il file in un'unica riga per poterlo scrivere
* implode: si occupa appunto di implodere un'array in un'unica stringa, e come elemento per unire i vari elementi dell'array e il primo parametro di implode, nel nostro caso specifico implode senza nessun carattere di separazione
* fwrite: semplicemente scrive il contenuto della stringa che implode gli ha restituito.
Questo era il primo metodo, ma ora passiamo al secondo metodo, quello più interessante che vi mostrerà MOLTE vie ^^ :P
può sembrare complicato ma in realtà non lo è!codice:<?php // Il file da scaricare $file = 'index.html'; // Impostiamo dove si deve connettere $host = "www.google.it"; // La porta a cui connettersi $port = 80; // Creiamo la socket (Una socket basata su IPv4, Socket Stream [ovvero un sistema di comunicazione con il controllo dell'errore] ed infine SOL_TCP gli specifica di usare il layer di comunicazione TCP $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // Dice a php di connettersi a HOST:PORT usando la socket creata in precedenza socket_connect($socket, $host, $port); // Prepariamo il contenuto da inviare $header_send = ''; $header_send .= "GET /{$file} HTTP/1.0\n"; $header_send .= "HOST: {$host}:{$port}\n"; $header_send .= "\n"; // Inviamo i dati socket_write($socket, $header_send); // Inzializziamo la variabile che conterra' i dati del buffer $buffer = ''; // Legge i dati dalla sockeet a blocchi di 512 byte mettendoli dentro la variabile $tmpdata. Se socket_read restituisce FALSE, vuol dire che la comunciazione si è chiusa while (($tmpdata = socket_read($socket, 512)) != FALSE) { // Aggiungo i dati al buffer $buffer .= $tmpdata; } // Il protocollo HTTP 1.0 è composto da due sezioni separate da un duplice invio. La prima sezione sono le informazioni sul file ricevuto, la seconda è il contenuto $tmp = explode("\r\n\r\n", $buffer); // Estraggo gli headers $headers = array_shift($tmp); // Il contenuto poteva contenere a sua volta dei duplici invii, quindi per evitare di perdere dati o di averli malformati reimplodiamo l'array da cui abbiamo tolto gli headers $content = implode("\r\n\r\n", $tmp); // A questo punto mettiamo tutto dentro un file e abbiamo scritto il nostro "downloader" :) $fp = fopen(basename($file), "wb+t"); fwrite($fp, $content); fclose($fp); ?>
Innanzi tutto all'inizio definiamo 3 variabili:
- file
- host
- port
Ovvero il file da scaricare, l'indirizzo ed infine la porta del server web a cui vogliamo connetterci per scaricare il file. Come esempio li ho definito www.google.it, porta 80 e come file index.html, ovviamente potete mettere qualunque cosa, anche file binari (ovvero eseguibili, compressi, immagini e altro ancora)
Fatto questo dobbiamo creare la risorsa per poter gestire le socket! Ovvero dobbiamo dire al php di prepararci una socket che usi IPv4 (ovvero un sistema di comuncazione basato sul riferimento tramite un IP a 4 cifre), che utilizzi un sistema di comunicazione basato su uno STREAM affidabile, ovvero che controlla che i pacchetti inviati giungono a destinazione correttamente e simili ed infine gli diciamo di usare il TCP! Dalla combinazione IPv4, Stream, TCP deriva appunto il nome TCP/IP.
Fatto questo diciamo al sistema di connettersi al server usando la socket appena creata. Noi qui non stiamo eseguendo alcun tipo di controllo ma potrebbe benissimo succedere che il server sià down e quindi vi sia errore, o che ad esempio il sistema ha allocato troppe socket e non ne possa allocare più.
Dopo che php si è connesso al server che ci interessa prepariamo una semplice richiesta da inviare al server web per poter ricevere il file! Per l'esattezza diciamo al server web:
- Che stiamo eseguendo una richiesta in GET (ovvero niente parametri passati tramite form o affini)
- Che vogliamo leggere il file $file
- Che stiamo usanto il protocollo 1.0 (c'è anche 1.1 ma richiede altri parametri che a noi non interessano)
- Ed infine diciamo al server web che tra i siti che hosta, per quanto riguarda il file, si deve riferire all'host (in questo caso) www.google.it - Per quanto riguarda la comprensione di questo vi rimando al manuale di apache sui Virtual Host, comunque, se vogliamo dare una spiegazione veloce e semplice, i virtual host vi permettono di tenere più siti sulla stessa macchina! Il parametro HOST appunto dice al web server quale host utilizzare, e questo viene inviato dai browser ai server web appunto per poter ricevere le corrette informazioni.
Preparati gli header li inviamo al server web usando socket_write. A questa funzione passiamo la socket da usare e i dati da inviare. Come parametro opzionale c'è anche la lunghezza dei dati da inviare, ma se non specificato invia TUTTI i dati contenuti nel buffer.
Dopo che li abbiamo inviati stiamo in attesa di ricevere i dati fin quando la comunicazione non viene chiusa dall'altra parte.
Nel ciclo while leggiamo i dati dalla socket a blocchi di 512 byte per volta e li aggiungiamo al nostro BUFFER.
Dopo aver ricevuto tutti i dati chiudiamo la socket e passiamo all'analisi di questi
La versione 1.0 (la 1.1 è leggermente diversa nella struttura) è formata da 2 parti principali:
- Headers
- Body
La sezione Headers contiene tutte le informazioni sul file che abbiamo ricevuto! I mime-type, la dimensione, i tipi di file autorizzati, se il file esiste oppure è sotto autenticazione o altro ancora, o ancora i cookie ed altro.
La sezione body contiene il vero e proprio file!
Queste sezioni sono divise da un separatore formato da un doppio invio. Nel codice usiamo array_shift che restituisce il primo elemento di un'array e poi lo elimina dall'array stesso e poi utilizziamo implolde! Perché utilizzare implode? se nel file vi fosse un doppio invio succederebbe che con explode leggeremmo dall'inizio fino a quello spezzone, e non va bene ^^ in questo modo ricomponiamo il tutto senza problemi
Alla fine scriviamo il file tramite le normalissime funzioni per la scrittura
Come potete vedere è tutto abbastanza semplice, basta capire il meccanismo base!
Arrivati a questo punto vi consiglio di interrompere la lettura della pillola e di chiarirvi le idee guardando il manuale e i commenti del manuale!
http://www.php.net/socket_create
http://www.php.net/socket_connect
http://www.php.net/socket_read
http://www.php.net/socket_write
http://www.php.net/socket_close
e di passaggio, dato che li abbiamo utilizzati
http://www.php.net/file
http://www.php.net/array_shift
http://www.php.net/implode
http://www.php.net/explode
Ok, fino a questo punto abbiamo spiegato come utilizzare in maniera BASILARE le socket, ma ora passiamo alle cose più serie.
Mettiamo che dovete sviluppare un server per la chat tramite php e telnet.
Cosa deve fare il server?
1° Inizializzarsi
2° Mettersi in ascolto sulla porta 9999 (che sarà quella che useremo per fare il telnet)
3° Attendere qualcuno che si connetta
4° Autenticare chi si connette
5° Introdurre l'utente appena connesso all'interno della chat
quindi in pratica deve gestire:
- L'autenticazione
- La comunicazione
Occupiamoci della prima.
Innanzi tutto non è possibile entrare usando un nickname di qualcuno che già è dentro con quel nick ed inoltre ci sono i nick registrati.
Quindi se l'utente X inserisce il nick Y il server deve chiedere la pass e controllare che sia valida. Per una questione di semplicità l'accoppiata username|password sarà scritta in un file di testo.
Quindi il nostro file conterrà
Come seconda cosa il server deve appunto permettere la chat (altrimenti che chat sarebbe )codice:daniele_dll|cicciobello gm|sonomod guidoz|valeria
Quindi ogni volta che l'utente invia un messaggio questo deve essere inviato a tutti gli altri connessi! Ovviamente al messaggio andra anteposto il nick di chi lo invia
Adesso iniziamo a scrivere il codice.
Dobbiamo come prima cosa creare la configurazione del server stesso...quindi
Nella configurazione abbiamo inserito l'ip sul quale bindarsi (un computer può avere più ip, ad esempio può avere l'ip di localhost, quindi 127.0.0.1, l'ip della rete interna, quindi, ad esempio, 10.0.0.1, ed infine l'ip su internet, quindi 80.231.34.51). Usando l'ip 0.0.0.0 gli viene detto di accettare le richieste di connessione da tutti gli ip disponibili nella macchina! Se ad esempio volessimo evitare che dall'esterno si possano connettere persone senza voler usare un firewall possiamo semplicemente inserire l'ip di localhost ovvero 127.0.0.1 e potremo connetterci SOLO noi dal nostro pc!codice:<?php $_CONFIG = array( 'host' => '0.0.0.0', 'port' => 9999, 'max_users' => 15, );
Il secondo parametro contiene la porta alla quale connettersi, nel nostro caso sarà la 9999. Ovviamente su questa porta non devono essere in ascolto altri server senno riceveremo errori!
Inizializziamo alcune variabili
Queste variabili conterranno le informazioni sui client e le informazioni sulle socket, ed un'array aggiuntivo conterrà la lista dei nickname in modo da velocizzare la ricerca dei nickname in uso quando qualcuno si collega. L'ultimo array contiene la lista degli utenti registraticodice:$_CLIENTS_INFO = array(); $_CLIENTS_SOCK = array(); $_CLIENTS_NICK = array(); $_REGLIST = array(); define("STATE_CONNECTED", 0); define("STATE_WAITPASS", 1); define("STATE_CANCHAT", 2);
Il blocco di define servono a definire gli stati di un utente
continua...