Bonjour,
Je développe une petite architecture client serveur et je travailles avec deux thread : AuthListener et AuthWorker.
Le premier accepte les connexions entrantes et vérifie si le message reçu est bien de type "demande de connexion". A partir de la il envoi la socket au deuxième thread qui va faire un traitement sur la base de donnée.
Voici mon code de la classe "écouteur"
#include "AuthListener.h"
AuthListener::AuthListener(int port,AuthWorker* authWorker) : selector(), tcpListener(), port(port), isListening(true), mutexListening()
{
}
void AuthListener::runListening()
{
// lie l'écouteur à un port
if (tcpListener.listen(400) != sf::Socket::Done)
{
// erreur...
}
selector.add(tcpListener);
//tant que on ecoute, on boucle pour accepter les nouveaux clients
while (isListening)
{
// socket TCP d'écoute de connexions des sockets clients:
if (selector.wait(sf::milliseconds(60)))
{
if (selector.isReady(tcpListener))
{
sf::TcpSocket* socket = new sf::TcpSocket();
if (tcpListener.accept(*socket) == sf::Socket::Done)
{
std::cout << "nouveau client connecté" << std::endl;
clients.push_back(socket);
//on ajoute la socket au selector
selector.add(*socket);
}
else
{
//erreur
delete socket;
}
}
//on a pas recu de nouvelles connexions de sockets donc on regarde si on a pas reçus de messages de connexions des sockets TCP connectés
else
{
// The listener socket is not ready, test all other sockets (the clients)
for (std::list<sf::TcpSocket*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
//std::cout << "test 0" << std::endl;
sf::TcpSocket& client = **it;
if (selector.isReady(client))
{
std::cout << "test 2" << std::endl;
// The client has sent some data, we can receive it
sf::Packet packet;
if (client.receive(packet) == sf::Socket::Done)
{
std::cout << "test 1" << std::endl;
//on teste l'opcode du message...
//si c'est bien une demande de connexion, on traite sinon on rejette
sf::Uint32 opcode;
packet >> opcode;
if (opcode == AuthCodes::CMSG_LOGIN_DEMAND)
{
std::cout << "opcode bon" << std::endl;
//on envoi la demande à AuthWorker qui va traiter la demande
authWorker->appendClient(&client, packet);
//on enleve le socket du selector
selector.remove(client);
}
}
}
}
}
}
}
}
Voici mon code de la classe qui traite les données
#include "AuthWorker.h"
AuthWorker::AuthWorker() : isWorking(true)
{
}
void AuthWorker::runWorker()
{
//on traite les données de la map
std::string user;
std::string password;
sf::Uint32 opcode;
while (this->isWorking)
{
//on parcourt la map pour traiter chaque clients puis on le transfert et on delete la map
std::map<sf::TcpSocket*, sf::Packet>::iterator pos;
for (pos = clientsMap.begin(); pos != clientsMap.end(); ++pos)
{
//on traite le paquet
pos->second >>opcode>>user>>password;
//on ne s'occupe pas de l'opcode car authlistener a deja fait le tri
//requete sql pour verifier que le compte existe et pour recuperer les infos du compte
std::cout << user << std::endl;
std::cout << password << std::endl;
}
}
}
void AuthWorker::appendClient(sf::TcpSocket* socket, sf::Packet& paquet)
{
mutexClients.lock(); // mutex.lock()
clientsMap.insert(std::pair<sf::TcpSocket*, sf::Packet>(socket, paquet));
mutexClients.unlock();
}
Et voici le code de la classe serveur (qui est créée dans le main et dont la fonction run() tourne pendant tout le programme.)
#include "Server.h"
Server::Server() : isRunning(true), authWorker(), authListener(400,&authWorker), threadAuthListener(0), threadAuthWorker(0)
{
}
bool Server::run()
{
threadAuthListener = new sf::Thread(&AuthListener::runListening, &authListener);
threadAuthWorker = new sf::Thread(&AuthWorker::runWorker, &authWorker);
//on lance le thread pour le authlistener
threadAuthListener->launch();
//on lance le thread pour le worker
threadAuthWorker->launch();
while (isRunning)
{
}
return true;
}
Lorsque je tente de me connecter avec un client ( dont voici le code)...
sf::TcpSocket socket;
sf::Socket::Status status = socket.connect(sf::IpAddress::getLocalAddress(), 400);
if (status != sf::Socket::Done)
{
std::cout << "erreur socket " << std::endl;
}
else
{
sf::Packet paquet;
paquet << 0x00 << "user" << "password";
if (socket.send(paquet) == sf::Socket::Done)
{
std::cout << "message envoyée " << std::endl;
}
}
Mon serveur plante à la ligne 72 du AuthListener.
Apres avoir affiché
test 1.
opcode bon.
C'est a dire à la ligne
//on envoi la demande à AuthWorker qui va traiter la demande
authWorker->appendClient(&client, packet);
Il me rapporte ce rapport de débuggage (je ne vous mets que la partie importante)
- this 0x0000000c {tcpListener={...} selector={m_impl=??? } port=??? ...} AuthListener *
__vfptr <Impossible de lire la mémoire> void * *
- tcpListener {...} sf::TcpListener
+ sf::Socket {m_type=??? m_socket=??? m_isBlocking=??? } sf::Socket
- selector {m_impl=??? } sf::SocketSelector
m_impl <Impossible de lire la mémoire>
port <Impossible de lire la mémoire>
isListening <Impossible de lire la mémoire>
+ mutexListening {m_mutexImpl=??? } sf::Mutex
+ clients { size=??? } std::list<sf::TcpSocket *,std::allocator<sf::TcpSocket *> >
authWorker <Impossible de lire la mémoire>
Alors bien entendu je me dis que j'ai du faire une grosse bourde...Mais honnetement je vois pas très bien.
Car je comprends pas trop pourquoi mon listener fais tout planter alors que il envoit juste des informations au worker. Est ce du à une mauvaise utilisation des mutex?
Pourquoi mon selector n'est plus valide?
J'ai réessayé mon code en enlevant le contenu de appendClient();
et son appel ne fait pas tout planter.
Du coup il me semble que je n'ai pas compris l'utilisation des mutexs..
En passant n'hésitez pas à critiquer mon code et mon utilisation des mutex c'est la première fois que je tente de faire du concurrentiel.
Merci beaucoup pour vos réponses!