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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php
/************************************************************
copyright claudio cardinale 2004-2011! tutti i diritti reservati
contatti :  cardi@thecsea.it(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