Précédent   Forum des professionnels en informatique > PHP > Langage
Langage Forum sur le langage PHP, la POO, les conventions, la sécurité, etc. Avant de poster : FAQ Langage, toutes les FAQ PHP, cours langage et sources PHP
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 28/11/2011, 13h07   #1
Invité de passage
 
Inscription : janvier 2009
Messages : 12
Détails du profil
Informations personnelles :
Âge : 23
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : janvier 2009
Messages : 12
Points : 3
Points : 3
Par défaut Socket Lire et ecrire avec le même navigateur

Bonjour,

J'ai réalisé des sockets php (client et serveur) mais je me heurte à un problème, lorsque dans mon onglet 1 j'attends des données, je ne peux pas écrire avec une nouvelle socket grâce à l'onglet 2.

Je ne sais pas si l'option non block peut résoudre mon problème, je n'ai pas bien compris comment m'en servir.

Par ailleurs, lorsque j'écoute mon serveur, dès que j'ai la réponse, je ferme ma connexion, ce n'est pas comme ceci que Stephane Eyskens fait, il affiche l'ip:port dans une frame, recupère le contenu d'avant et ajout le nouveau message.

Serveur :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/local/php53/bin/php-cgi -q
<?php
 
set_time_limit (0);
ob_implicit_flush();
require_once 'includes/application_top.php';
error_reporting(E_ALL);
// SOCKET_SERVER localhost
// SOCKET_PORT_ORDER 8081
$socket = new Socket(SOCKET_SERVER, SOCKET_PORT_ORDER); // include/configure.php && administration/include/configure.php
$socket->start();
$socket->listen();

Client (lecture) :

Code :
1
2
$socket = new Socket(SOCKET_SERVER, $_REQUEST['port']);
$socket->read();
Client (écriture) :

Code :
1
2
$socket = new Socket('localhost', '8081');
var_dump($socket->write('Bonjour : ' . date('H:i:s')));
La console serveur :

Code :
1
2
3
4
5
6
Nouveau client connecté : Resource id #66
Total des clients : 1
Message reçu de Resource id #67 : Bonjour : 12:48:07
Diffusion du message Bonjour : 12:48:07
Ressource Resource id #66...OK
Fermeture de la socket Resource id #67
Voici la classe que j'utilise :

Code :
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
class Socket {
 
    private $adress;
    private $port;
    private $socket;
    private $clients = array();
    private $buffer  = 128;
    private $new     = 'yo';
 
    public function __construct($adress, $port) {
        $this->adress = $adress;
        $this->port   = $port;
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    }
 
    /**
     * Démarre la socket et l'écoute
     * @return  Mixed   String erreur | rien
     */
    public function start()
    {
        echo PHP_EOL;
        echo '==============================';
        echo PHP_EOL;
        echo 'Création de la socket...';
        if ($this->socket === false) {
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_create() a échoué raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            exit;
        } else {
            echo 'OK' . PHP_EOL;
        }
 
        echo 'Connexion à la socket ' . $this->adress . ':' . $this->port . '...';
        if (!socket_bind($this->socket, $this->adress, $this->port)) {
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_bind() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            socket_close($this->socket);
            exit;
        } else {
            echo 'OK' . PHP_EOL;
        }
 
        echo 'Ecoute de la socket...';
        if (!socket_listen($this->socket)) { // Start listening for connections
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_listen() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            socket_close($this->socket);
            exit;
        } else {
            echo 'OK' . PHP_EOL;
        }
        echo '==============================' . PHP_EOL;
    }
 
    /**
     * A démarrer sur le serveur #!/usr/local/php53/bin/php-cgi -q
     * Ecoute l socket
     * @return type
     */
    public function listen()
    {
        echo 'Prêt à écouter les clients';
 
        if ($this->socket === false) {
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'listen() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            $this->closeSocket();
            exit;
        }
 
        echo '...OK' . PHP_EOL;
        echo 'Nombre de client : ' . count($this->clients) . PHP_EOL;
        echo '==============================' . PHP_EOL;
 
        while (true) : // Loop continuously
 
            if (!($ressource = socket_accept($this->socket))) { // plus de socket
                $error = socket_last_error($this->socket);
                if ($error != 11 && $error != 115) {
                    echo PHP_EOL . PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_accept() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror($this->socket) . PHP_EOL;
                }
                //exit;
            } else {
                $this->readAndWrite($ressource);
            }
 
        endwhile;
    }
 
    /**
     * Récupère les nouvelles sockets
     * @param Socket $ressource
     */
    private function readAndWrite($ressource)
    {
        if (!is_resource($ressource)) {
            echo PHP_EOL . PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'is_resource() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            //exit;
        }
 
 
        // si ecriture on transmet
        // sinon ecoute, on le stock avec les autres.
 
        $buffer = socket_read($ressource, $this->buffer);
 
        if (!empty($buffer) && $buffer === 'kill') {
            $this->clients[] = $ressource;
            echo 'Kill...' . PHP_EOL;
            $this->closeSocket();
        } elseif (!empty($buffer) && $buffer != $this->new) {
            echo 'Message reçu de ' . $ressource . ' : ' . $buffer . PHP_EOL;
            $this->writeToClient($buffer);
            echo 'Fermeture de la socket ' . $ressource . PHP_EOL;
        } elseif (!in_array($ressource, $this->clients)) { // Nouveau client
            $this->clients[] = $ressource;
            echo 'Nouveau client connecté : ' . $ressource . PHP_EOL;
            echo 'Total des clients : ' . count($this->clients) . PHP_EOL;
        }
    }
 
    /**
     * Notification de à tous les clients connectés
     * @param String $msg
     */
    private function writeToClient($msg)
    {
        echo 'Diffusion du message ' . $msg . PHP_EOL;
 
        foreach ($this->clients as $key => $socket) :
            if (!is_resource($socket)) {
                unset($this->clients[$key]);
            } else {
 
                echo 'Ressource ' . $socket . '...';
 
                if (!@socket_write($socket, $msg, strlen($msg))) {
                    //echo PHP_EOL . PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_write() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
                    unset($this->clients[$key]);
                    echo 'ECHEC : ressource fermée' . PHP_EOL;
                } else {
                    echo 'OK' . PHP_EOL;
                }
 
            }
        endforeach;
    }
 
    /**
     * Déconnecte tous les clients
     */
    private function disconnectAllClient()
    {
        echo 'Nombre de client : ' . count($this->clients) . PHP_EOL;
        $this->writeToClient('Le serveur va être arrêté.');
 
        foreach ($this->clients as $key => $socket) :
            if (!is_resource($socket)) {
                echo 'Déconnexion du client : ' . $socket . PHP_EOL;
                @socket_close($socket);
            }
        endforeach;
    }
 
    /**
     * Ferme la socket mère
     */
    private function closeSocket()
    {
        $this->disconnectAllClient();
 
        if ($this->socket !== null) {
            echo 'Fermeture de la session...';
            socket_shutdown($this->socket, 1);//remote host yet can read
            usleep(500);//wait remote host
            socket_shutdown($this->socket, 0);//close reading
            socket_close($this->socket);//finaly we can free resource
            echo 'OK' . PHP_EOL;
        }
 
        exit(PHP_EOL . 'Socket fermée.' . PHP_EOL);
    }
 
    /**
     * Client
     */
 
    public function read()
    {
        if (!@socket_connect($this->socket, $this->adress, $this->port)) {
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_connect() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            return null;
        }
 
        socket_write($this->socket, $this->new, strlen($this->new));
 
        $i = 0;
 
        while ($i < 10) :
            echo socket_read($this->socket, $this->buffer);
            sleep(1);
            $i++;
        endwhile;
 
        echo '<hr>';
 
        socket_close($this->socket);
    }
 
    public function write($msg)
    {
        if (!@socket_connect($this->socket, $this->adress, $this->port)) {
            //echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_connect() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            return null;
        }
 
        if (!@socket_write($this->socket, $msg, strlen($msg))) {
            //echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_write() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
        }
 
        socket_close($this->socket);
 
        return true;
    }
}
Bazou00 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/11/2011, 17h00   #2
Membre chevronné
 
Avatar de Marc3001
 
Homme
Ingénieur développement logiciels
Inscription : février 2008
Messages : 430
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 28
Localisation : France, Ille et Vilaine (Bretagne)

Informations professionnelles :
Activité : Ingénieur développement logiciels

Informations forums :
Inscription : février 2008
Messages : 430
Points : 682
Points : 682
Citation:
Envoyé par Bazou00 Voir le message
J'ai réalisé des sockets php (client et serveur) mais je me heurte à un problème, lorsque dans mon onglet 1 j'attends des données, je ne peux pas écrire avec une nouvelle socket grâce à l'onglet 2.
Citation:
Envoyé par Bazou00 Voir le message
Par ailleurs, lorsque j'écoute mon serveur, dès que j'ai la réponse, je ferme ma connexion, ce n'est pas comme ceci que Stephane Eyskens fait, il affiche l'ip:port dans une frame, recupère le contenu d'avant et ajout le nouveau message.
Ton code php s'éxécute sur le serveur web. A partir du moment où ta page est affichée sur ton navigateur, ton script a terminé toute son éxécution.

Comment as-tu architecturé tes scripts? Quel morceaux dans quelle page? Lances-tu tous tes scripts via navigateur ou certains (serveur) via CLI?
__________________
Le logiciel, c'est comme le sexe, c'est meilleur quand c'est libre.

Linus Torvalds
Marc3001 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 06/12/2011, 10h10   #3
Invité de passage
 
Inscription : janvier 2009
Messages : 12
Détails du profil
Informations personnelles :
Âge : 23
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : janvier 2009
Messages : 12
Points : 3
Points : 3
Bonjour,

Merci de t'intéresser à mon problème. Désolé pour la latence de la réponse, je pensais être notifié par mail.

Je lance en CLI le code serveur (Cf. le premier paragraphe de code).

J'ai trouvé une solution pour passer outre, mais elle n'est pas utilisable en prod.

Voici une des applications des sockets :
  • J'ai une page avec ma liste des commandes
  • Une autre avec le détail de ma commande

Elles sont chacune exécuté sur des postes client. Un commercial peut s'attribuer une commande, alors la liste des commandes doit s'actualiser sur tous les navigateurs l'affichant.

Aujourd'hui, je stockes tous les messages avec l'ID de la commande et le nom du commercial, toutes les 1 secondes, ma socket va demander au serveur les messages et mets à jour les lignes de la liste des commandes.

On perd pas mal l'intérêt de la mise à jour lorsqu'il y a une info.


Le problème c'est que si j'attends l'info avec une boucle, cela empêche de mettre à jour le détail d'une commande.

Ma liste boucle pour attendre l'info, tant qu'elle boucle, le même navigateur internet dans un onglet différent du poste client ne peut pas écrire au serveur.
Bazou00 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2011, 18h12   #4
Candidat au titre de Membre du Club
 
Homme
Inscription : octobre 2007
Messages : 10
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : octobre 2007
Messages : 10
Points : 10
Points : 10
Salut Bazou,

Ta socket doit simplement accepter les connexions/opérations non bloquantes
Et donc ton constructeur doit être

Code :
1
2
3
4
5
6
7
8
 
public function __construct($adress, $port) {
        $this->adress = $adress;
        $this->port   = $port;
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        socket_set_nonblock($socket);
        $this->socket = $socket;
}
Tu dois donc adapter l'écoute de chaque client

Code :
1
2
3
4
5
6
7
8
9
10
11
12
 
 public function read()
    {
        if (!@socket_connect($this->socket, $this->adress, $this->port)) {
            echo PHP_EOL . __FILE__ . ':' . __LINE__ . PHP_EOL . PHP_EOL . 'socket_connect() a échoué adresse : ' . $this->adress . ':' . $this->port . ' raison : ' . socket_strerror(socket_last_error()) . PHP_EOL;
            return null;
        }
 
        socket_write($this->socket, $this->new, strlen($this->new));
 
        return ($data = socket_read($this->socket, $this->buffer));
    }

Code :
1
2
3
4
5
6
7
8
$socket = new Socket(SOCKET_SERVER, $_REQUEST['port']);
if ($socket->read() === FALSE){
    //aucune donnee recue
}
else{
    //msgs recus
}
Et pour le refresh auto tu peux le faire via Javascript toutes les 10s sur chaque page cliente

PS:
Tu devrais avoir une classe abstraite Socket, une SocketClient, et une SocketServer.

PS2:
Ta conception du problème initial (Gestion de commandes client/fournisseur
en gros) est vraiment à revoir. Une simple architecture utilisant une BD,
ajax et PHP est proprement interessante/scalable/efficace.
foberken est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2011, 18h45   #5
Invité de passage
 
Inscription : janvier 2009
Messages : 12
Détails du profil
Informations personnelles :
Âge : 23
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : janvier 2009
Messages : 12
Points : 3
Points : 3
Le problème de le mettre les sockets en non bloquantes c'est que je ne reçois jamais les messages, car je fais lecture, clôture.

Comment est-ce que je peux lire, rester connecté et lire quand même mon message arrive ? Tout en gardant la possibilité d'envoyer.
  • Car si je fais un while en lecture, je reçois la réponse, mais pendant ce temps, je ne peux pas écrire.
  • Si je refresh ma page, je perds mon instance de socket.

Tu as raison pour la classe abstraite, je vais reprendre tout ça.

Pour ce qui est de l'appel en BD, à la base, je voudrais mettre à jour les infos dès qu'elles arrivent, sans faire un refresh toutes les X secondes. Actuellement c'est une table en memory. Mais le système se développera et évoluera surement pour d'autres choses, je voudrais donc ne pas utiliser de settimeout et faire du push d'information (sans installer un serveur apache différent comme ape ou utiliser node.js).
Bazou00 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2011, 19h33   #6
Candidat au titre de Membre du Club
 
Homme
Inscription : octobre 2007
Messages : 10
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : octobre 2007
Messages : 10
Points : 10
Points : 10
Citation:
Si je refresh ma page, je perds mon instance de socket
Dans Socket::read() les 3 1ères lignes s'occupent de se connecter
(maladroitement d'ailleurs) à chaque tentative de lecture sur le serveur.

Donc à chaque refresh en JS (coté CLIENT) tu as une nvelle demande de connexion et une nvelle tentative de lecture; Sans passer par une boucle active.


Citation:
je voudrais mettre à jour les infos dès qu'elles arrivent,
sans faire un refresh toutes les X secondes
Bah le principe même de l'http1.1 c'est de faire des Request et d'attendre des réponses. En utilisant une BD, un script de lecture (qui de renvoi les nouvelles entrées) et un peu d'Ajax, tu résouds ton problème et tu rends le refresh transparent aux yeux de l'user.
foberken est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/12/2011, 19h48   #7
Invité de passage
 
Inscription : janvier 2009
Messages : 12
Détails du profil
Informations personnelles :
Âge : 23
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : janvier 2009
Messages : 12
Points : 3
Points : 3
Citation:
Envoyé par foberken Voir le message
(maladroitement d'ailleurs)
Comment le faire adroitement ?

Le problème d'un refresh c'est que si l'utilisateur ouvre 50 onglets, le serveur est bouché (car 50 appel toutes les X secondes), pour rien, car potentiellement il n'y a pas de nouvelle information.



Ok pour le principe de l'HTTP, mais je veux étendre ce principe, au lieu d'avoir du poll :

client -> serveur, donne moi les infos

je veux du push :

serveur -> tient client, voici les nouvelles infos.


Cela est possible avec les sockets, puis que la socket master est capable de notifier tous les clients qui sont connectés. Si mes clients ne devaient pas lire ET écrire en même temps, tout irai bien.
Bazou00 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/12/2011, 17h20   #8
Membre chevronné
 
Avatar de Marc3001
 
Homme
Ingénieur développement logiciels
Inscription : février 2008
Messages : 430
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 28
Localisation : France, Ille et Vilaine (Bretagne)

Informations professionnelles :
Activité : Ingénieur développement logiciels

Informations forums :
Inscription : février 2008
Messages : 430
Points : 682
Points : 682
Le gros souci que je trouve à ta solution c'est la connexion directe des clients à ton socket serveur.
Certes tu as du vrai push mais ta solution nécessite une ouverture de flux directe du client vers le serveur sur le port du socket serveur.
Donc nécessité d'ouverture firewall et utilisation d'un proxy compliquée.

Citation:
Le problème d'un refresh c'est que si l'utilisateur ouvre 50 onglets, le serveur est bouché (car 50 appel toutes les X secondes), pour rien, car potentiellement il n'y a pas de nouvelle information.
Finalement ton socket serveur fait le même boulot qu'Apache à part que la connexion est permanente. Je pense réellement qu'Apache est largement optimisé pour gérer ces connexions et que si tu as de grosses sollicitations Apache tiendra mieux la charge que ton socket.
__________________
Le logiciel, c'est comme le sexe, c'est meilleur quand c'est libre.

Linus Torvalds
Marc3001 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/12/2011, 17h39   #9
Invité de passage
 
Inscription : janvier 2009
Messages : 12
Détails du profil
Informations personnelles :
Âge : 23
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : janvier 2009
Messages : 12
Points : 3
Points : 3
Je ne veux pas refresh toutes les X secondes, se sera beaucoup trop lourd pour notre faible MySQL.



Est-ce qu'il est possible d'avoir du temps réel ?
Connais-tu un moyen ?
Comment procèdent Facebook et GoogleDoc ?
Bazou00 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 08/12/2011, 18h43   #10
Membre chevronné
 
Avatar de Marc3001
 
Homme
Ingénieur développement logiciels
Inscription : février 2008
Messages : 430
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 28
Localisation : France, Ille et Vilaine (Bretagne)

Informations professionnelles :
Activité : Ingénieur développement logiciels

Informations forums :
Inscription : février 2008
Messages : 430
Points : 682
Points : 682
Je suis presque sûr que google et facebook n'utilisent pas de socket mais bien de l'ajax avec du refresh toutes les x secondes.....
__________________
Le logiciel, c'est comme le sexe, c'est meilleur quand c'est libre.

Linus Torvalds
Marc3001 est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 07h59.


 
 
 
 
Partenaires

Hébergement Web