Prima di proseguire...
Potrebbe interessarti la nostra collezione di esercizi C risolti?
Del server di chat è stata realizzata una nuova versione
Oggi vediamo come una chat in php usando le socket di basso livello, naturalmente usando il php da terminale.
Per comodità di utilizzo e di schematizzazione del codice ho creato una classe.
il codice è il seguente:
<?php /************************************************************ copyright claudio cardinale 2004-2011! tutti i diritti reservati contatti : [email protected](o http://www.thecsea.it) versione : 0.10.0 data : 16/02/2011 16:51 *************************************************************/ class Server{ //proprieta private private $indirizzo = ""; private $porta = ""; private $sock = ""; private $clienti = array(); private $i = 0; //costruttore public function __construct($indirizzo, $porta){ $this->indirizzo = $indirizzo; $this->porta = $porta; print "avvio server in corso...\n"; set_time_limit(0); ob_implicit_flush(); $this->sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() fallito: motivo: " . socket_strerror($this->sock) . "\n"); $ret = @socket_bind($this->sock, $this->indirizzo, $this->porta) or die("socket_bind() fallito: motivo: " . socket_strerror($ret) . "\n"); $ret = @socket_listen($this->sock, 5) or die("socket_listen() fallito: motivo: " . socket_strerror($ret) . "\n"); $ret = @socket_set_nonblock($this->sock) or die("socket_set_nonblock() fallito: motivo: " . socket_strerror($ret) . "\n"); $this->ciclo(); } //distruttore public function __destruct(){ socket_close($this->sock); foreach($this->clienti as $key=>$value) $this->disconnetti($key); } //metodi privati //ciclo principale private function ciclo(){ print "server avviato...\n"; for(;;){ $this->aggiungi(); $this->chat(); usleep(5); } } //aggiunta clienti private function aggiungi(){ if($buf = @socket_accept($this->sock)){ $this->clienti[$this->i] = $buf; $msg = "\nBenvenuto in chat...\nscrivere quit per uscire...\n"; if($this->scrivi($buf, $msg)===false){ if($this->disconnetti($this->i)) print "errore con il cliente ".($this->i+1)." (in scrittura) -> disconnesso correttamente\n"; else print "errore con il cliente ".($this->i+1)." (in scrittura) -> impossibile disconnetterlo\n"; return false; } $this->i++; print "cliente ".$this->i." aggiunto\n"; return true; } } //richiamo metodi per chat private function chat(){ //controllo se ci sono clienti if(!$this->clienti) return false; //controllo cambiamenti $read = $this->clienti; $var = null; $var2 = null; socket_select($read,$var,$var2,0); //lettura clienti foreach($read as $key1=>$value1){ $ret = $this->leggi($value1); //key corrispondente nell'array clienti $key_o = array_search($value1, $this->clienti); //uscita if(trim($ret)=="quit") if($this->disconnetti($key_o)) print "cliente ".($key_o+1)." disconesso correttamente\n"; else print "errore con cliente ".($key_o+1)." (in disconnessione)\n"; //stampa sui clienti else if($ret) foreach($this->clienti as $key2=>$value2) if($key_o!=$key2 && $this->scrivi($value2, $ret)===false) if($this->disconnetti($key2)) print "errore con il cliente ".($key2+1)." (in scrittura) -> disconnesso correttamente\n"; else print "errore con il cliente ".($key2+1)." (in scrittura) -> impossibile disconnetterlo\n"; } return true; } //lettura private function leggi($sock){ return @socket_read($sock, 2048, PHP_NORMAL_READ); } //scrittura private function scrivi($sock, $mex){ return @socket_write($sock, $mex, strlen ($mex)); } //disconnessione cliente private function disconnetti($chiave){ if(@socket_close($this->clienti[$chiave])===false) return false; unset($this->clienti[$chiave]); return true; } } $server1 = new Server("127.0.0.1","9000"); $server1 = 0; ?>
Per chi vuole subito provarlo basta cambiare i parametri di new Server(“127.0.0.1″,”9000”) il primo indica l’indirizzo IP della macchina il secondo la porta.
Per avviarlo basta fare php serve.php oppure se lo si vuole avviare in background: nohup php server.php /dev/null 2>&1 &
Per connettersi come client basta eseguire un normale telnet alla porta specificata, esempio: telnet 127.0.0.1 900
N.B. Se si avvia il server usando come indirizzo IP l’indirizzo locale della macchina (es: 192.168.0.2) bisogna anche da telnet connettersi su quell’indirizzo e non su localhost(127.0.0.1) anche se ci si trova in locale
Naturalmente in seguito si può facilmente creare un client e fare varie migliorie come ad esempio una gestione degli utenti.
Dopo questa breve introduzione passiamo ad una spiegazione del codice.
Costruttore:
Il costruttore non fa altro che creare una socket tcp sulla porta specificata:
$this->sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() fallito: motivo: " . socket_strerror($this->sock) . "\n"); $ret = @socket_bind($this->sock, $this->indirizzo, $this->porta) or die("socket_bind() fallito: motivo: " . socket_strerror($ret) . "\n");
in seguito si mette in ascolto permettendo di avere una coda massima di 5 client, togliendo il blocco in modo da poter andare avanti nelle azioni:
$ret = @socket_listen($this->sock, 5) or die("socket_listen() fallito: motivo: " . socket_strerror($ret) . "\n"); $ret = @socket_set_nonblock($this->sock) or die("socket_set_nonblock() fallito: motivo: " . socket_strerror($ret) . "\n");
infine richiama il metodo ciclo che, come vedremo, contiene al suo interno un ciclo infinito
Ciclo:
non fa altro che creare un ciclo infinito che ogni volta richiama i metodi per vedere se aggiungere clienti in chat e per vedere se ci sono nuovi messaggi in attesa di essere recapitati, infine fa una piccola pausa di 5 millisecondi per non sovraccaricare troppo il processore.
Aggiungi:
Verifica se un client ha tentato di connettersi (con socket_accept($this->sock)) se è così lo aggiunge alla lista dei clients, in seguito prova a vedere se la connessione funziona correttamente provando a scrivere un messaggio al client, se risultano esserci problemi lo disconnette completamente eliminandolo anche dalla lista.
Chat:
Grazie alla socket_select verifica se dei clients hanno inviato qualcosa:
$read = $this->clienti; $var = null; $var2 = null; socket_select($read,$var,$var2,0);
$read in questo caso contiene un vettore contenente i resource delle socket di quei client che hanno scritto qualcosa.
In seguito viene letto tutto il vettore $read leggendo gli input dal client e cercando ogni volta la chiave corrispondente nel vettore originale (clienti):
$ret = $this->leggi($value1); $key_o = array_search($value1, $this->clienti);
Poi viene controllato se l’utente desidera disconnettersi:
if(trim($ret)=="quit") if($this->disconnetti($key_o)) print "cliente ".($key_o+1)." disconesso correttamente\n"; else print "errore con cliente ".($key_o+1)." (in disconnessione)\n";
ed infine viene scritto il messaggio in tutti gli altri client, se non rispondono vengono disconnessi:
else if($ret) foreach($this->clienti as $key2=>$value2) if($key_o!=$key2 && $this->scrivi($value2, $ret)===false) if($this->disconnetti($key2)) print "errore con il cliente ".($key2+1)." (in scrittura) -> disconnesso correttamente\n"; else print "errore con il cliente ".($key2+1)." (in scrittura) -> impossibile disconnetterlo\n";
Leggi e scrivi:
sono dei metodi che non fanno altro che richiamare le corrispondenti funzioni delle scoket.
Disconnetti:
non fa altro che chiudere completamente la connessione con il client ed eliminarlo dalla lista
N.B. Per comodità di implementazione ogni messaggio può essere lungo al massimo 2048 caratteri
server di chat in php con i socket by cardinale claudio is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Complimenti per l’articolo, tutto molto chiaro.
Se posso permettermi vorrei farti una critica: i nomi delle funzioni, variabili e proprietà un po’ in italiano e un po’ in inglese, ti prego, NO! 🙂
grazie…
be’ le funzioni sono tutte in italiano, ogni tanto qualche variabile in inglese scappa, il fatto è che questo è nato come progetto scolastico e quindi ho cercato di farlo in italiano XD
Pingback: nuova versione server di chat in php con i socket « tutorial programmazione