J'éprouve quelques difficultés à le modifier. C'est la raison pour laquelle ce code est rempli de "printf" indiquant où en est le programme lorsque je l'éxécute.
Concrètement ce que je cherche à faire dans ce code c'est :
- connecter N clients à un serveur (ça j'y arrive)
- faire en sorte que chaque fois qu'un nouveau client se connecte tous les autres utilisateurs soient prévenus. (ça ça marche aussi)
- faire en sorte que chaque fois qu'un message est envoyé le serveur l'affiche dans sa console (puis à l'avenir qu'il le renvoie vers tous les clients afin de faire un mini-chat).
Et c'est ce dernier point qui me bloque.
Le code,une simple adaptation de celui de la page sur SocketSelector, est recopié ici :
#include <iostream>
#include <SFML/Network.hpp>
#include <list>
int main()
{
// Create a socket to listen to new connections
sf::TcpListener listener;
listener.listen(5000);
// Create a list to store the future clients
std::list<sf::TcpSocket*> clients;
// Create a selector
sf::SocketSelector selector;
// Add the listener to the selector
selector.add(listener);
// Endless loop that waits for new connections
while (true)
{
// Make the selector wait for data on any socket
if (selector.wait())
{
// Test the listener
if (selector.isReady(listener))
{
// The listener is ready: there is a pending connection
sf::TcpSocket* client = new sf::TcpSocket;
if (listener.accept(*client) == sf::Socket::Done)
{
// Add the new client to the clients list
clients.push_back(client);
// Add the new client to the selector so that we will
// be notified when he sends something
selector.add(*client);
sf::Packet packet;
std::string str = "Serveur : Un nouveau client s'est connecté.\n";
printf("%s",str.c_str());
packet << str;
int i=0;
for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
sf::TcpSocket& client = **it;
if (!(client.send(packet) == sf::Socket::Done))
{
printf("Le paquet contenant \"un nouveau client vient de se connecter n'a pas pu être envoyé au client %d\n\"",i);
}
else printf("Le paquet contenant l'info de nouvelle connexion a bel et bien été envoyé au client %d.\n");
i++;
}
}
else
{
printf("La connexion n'a pas pu être établie.\n");
// Error, we won't get a new connection, delete the socket
delete client;
}
}
else
{
// The listener socket is not ready, test all other sockets (the clients)
printf("On a reçu des données d'autre chose qu'en provenance du listeneur.\n");
int j = 0;
for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
sf::TcpSocket& client = **it;
if (selector.isReady(client))
{
printf("On a reçu des paquets venant du client %d\n", j);
// The client has sent some data, we can receive it
sf::Packet packet;
if (client.receive(packet) == sf::Socket::Done)
{
std::string msg;
packet >> msg;
std::cout << msg;
}
}
j++;
}
}
}
}
}
Le scénario est le suivant :
-Je connecte un premier client à mon serveur. Les printf que j'utilise m'indiquent que la connexion s'est bien produite. J'ai pu découvrir à ce propos que le client envoie deux messages suite à cette connexion. Je soupçonne qu'il s'agisse là d'accusés de réception : Client1 -> Serveur dit : "ok je me suis connecté" puis Client1 -> Serveur dit : "j'ai bien reçu ta chaîne de caractère indiquant qu'un nouveau client s'était connecté" (à savoir le client1 lui-même).
-J'envoie un message au serveur depuis mon client 1. Rien ne semble se produire.
-Je connecte un second client à mon serveur. Tout se passe bien. Et une fois ceci fait le message précédemment envoyé s'affiche.
1) Je ne comprends pas du tout pourquoi le message émis par le client 1 ne s'affiche dans la console du serveur qu'après que le client 2 se soit connecté. Est-ce qu'une personne bien informée pourrait m'en expliquer la raison ?
2) J'ai constaté que lorsque je quittais inopinément un programme client ( CTRL+C sous unix ) une boucle infinie se lançait dans le serveur. Je suppose que le serveur n'a pas été programmé pour prévoir un cas de déconnexion. Mais je ne comprends pas ce qui le fait entrer dans cette boucle et je ne sais pas comment résoudre ce problème. Pourriez-vous m'aider sur ce point également ?
Je vous remercie chaleureusement pour votre aide.
PS : ce message est un doublon d'un autre post publié ici : http://fr.sfml-dev.org/forums/index.php?topic=16873.0
Mais ce dernier forum semble beaucoup moins actif que celui d'open classroom.
// Create a selector
sf::SocketSelector selector;
// Add the listener to the selector
selector.add(listener);
// Endless loop that waits for new connections
Tu dois recréer le selector à chaque fois que tu veux t'en servir. Donc crée-le dans la boucle.
Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
J'ai essayé ce que tu viens de proposer mais cela ne fonctionne pas. Et c'est normal : si je réinitialise le selector à chaque tour de boucle, il n'y a aucune raison pour qu'il se souvienne des sockets enregistrés précédemment. Par conséquent mon serveur ne peut pas communiquer avec les clients puisqu'il efface tous les canaux de communications (sockets) avec eux de sa mémoire à chaque tour de boucle.
De plus l'auteur de la documentation n'a pas mis le selector dans la boucle.
Je crois donc ne pas comprendre ce que tu veux dire. Pourrais-tu me détailler un peu plus ta pensée ?
Oups ! En effet je me suis totalement trompé ! J'ai confondu avec autre chose...
Pour en revenir à ton problème, je t'avoue ne pas vraiment le comprendre. Il te suffit de boucler sur tes clients et d'envoyer le message sur client_courant != client_qui_a_recu_le_message.
PS: pourquoi utilises-tu printf dans du C++ alors que tu utilises cout à certains endroits ? printf c'est du C, ça n'a rien à faire dans ce code.
Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
Pour le printf, c'est juste parce que par le passé j'ai plus souvent eu l'occasion de faire du C que du C++ et du coup c'est un vieux réflexe. En effet je peux tout remplacer par des cout.
En fait ce que je veux comprendre c'est pourquoi ce code ne fait pas ce que je veux. Et ce que je veux c'est que dès qu'un client envoie un message le serveur le réceptionne et l'affiche dans sa console. Mais en l'occurrence je dois attendre qu'un nouveau client se connecte pour que le serveur affiche le message du premier client dans sa console. Je ne m'explique pas ce phénomène. J'ai pas mal essayé de modifier mon code mais rien n'y fait.
En fait c'était mon client qui avait un problème, pas mon serveur. Je te remercie franchement AlienX13.
Toutefois j'ai une dernière question. Quand depuis le logiciel client on veut fermer la connexion, alors comment le faire proprement ? Et si le client quitte subitement (par exemple si je fais CTRL+C dans le client) comment traiter ce problème avec mon serveur ? Actuellement en effet, ça fait juste boucler mon serveur infiniment sur un truc absurde. Réciproquement -mais c'est un cas plus rare en pratique- si le serveur plante, comment traiter le problème proprement depuis le client ?
Je suis débutant également donc je ne connais pas la bonne solution.
CTRL^C, est interceptable, son signal est SIGINT et tu peux appeler une fonction quand on le recoit. Une fonction qui enverrais aux clients que le serveur va très prochainement se deconnecter (tu peux faire pareil côté client).
Si le serveur se deconnecte vraiment sans t'en informer, le seul moyen de le savoir c'est lors d'un sf::TcpSocket::send() ou sf::TcpSocket::receive(),
@Pauda: C'est relativement simple en fait, d'apres le man, la fonction recv renvoie un nombre inferieur a 1 si le client est deconnecte (seul moyen de savoir si le client est toujours connecte d'ailleurs). Plus qu'a trouver l'equivalent avec la sfml !
Pour ceux qui souhaiteraient apprendre à développer en Rust, un tuto en français est dispo ici. Pour voir mes projets : github.
Merci à tous pour vos réponses. C'était très intéressant. Je passe le sujet en résolu.
SFML: Petite incompréhension avec SocketSelector.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C