nuova versione server di chat in php con i socket 1


Alcuni giorni fa avevo pubblicato una versione di un server di chat in php con i socket. Oggi pubblico una versione migliorata. Che al contrario dell’altra non rallenta la CPU, in quanto si ferma fin quando non si connette un nuovo client o vengono ricevuti nuovi dei messaggi.

ecco il codice:

<?php
/************************************************************
copyright claudio cardinale 2004-2011! tutti i diritti reservati
contatti : 	[email protected](o http://www.thecsea.it)
versione :	1.0.0
data :		21/02/2011 18:01
*************************************************************/
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";
		$var = null;
		$var2 = null;
		
		for(;;){
			//controllo cambiamenti
			$read = array_merge(array($this->sock),$this->clienti);
			socket_select($read,$var,$var2,null);
			
			//aggiunta clienti
			if(($key=array_search($this->sock,$read))!==false){
				unset($read[$key]);
				$this->aggiungi();
			}
			
			//chat
			if($read!==false)
				$this->chat($read);
		}
	}
	
	//aggiunta clienti
	private function aggiungi(){
		//controllo nuovi clienti
		if($buf = @socket_accept($this->sock)){
			//aggiunta cliente alla lista
			$this->clienti[$this->i] = $buf;
			
			//stampa e controllo se il cliente è connesso correttamete
			$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;
			}

			//ritorno
			$this->i++;
			print "cliente ".$this->i." aggiunto\n";
			return true;
		}
		return false;
	}
	
	//richiamo metodi per chat
	private function chat($read){		
		//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";
			
			//errore in lettura
			}else
				if($this->disconnetti($key_o))
					print "errore con il cliente ".($key_o+1)." (in lettura) -> disconnesso correttamente\n";
				else
					print "errore con il cliente ".($key_o+1)." (in lettura) -> 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;
?>

In pratica invece di fare la socket_select solo sui socket dei clients la faccio anche sul socket principale, controllando poi con un if se ci sono stati cambiamenti nel socket principale. Se ci sono stati elimino il socket dal vettore read, in modo da lasciarlo con i soli socket dei clients, in seguito aggiungo i clients. Infine eseguo normalmente il metodo chat(se ci sono nuovi messaggi), come prima con l’unica differenza che il vettore read glielo passo come parametro, dato che è già stato calcolato.

N.B. è stato fatto anche un’altro miglioramento: nel caso in cui la la lettura restituisce un valore nullo o falso il client viene disconnesso

P.S. una versione leggermente modificata di questo codice è stata usata per creare un videogioco multiplayer

CC BY-SA 4.0 nuova versione server di chat in php con i socket by cardinale claudio is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.


Lascia un commento

Un commento su “nuova versione server di chat in php con i socket