Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: sf::Socket::Error après quelques secondes de connexion  (Lu 2912 fois)

0 Membres et 1 Invité sur ce sujet

Glaucos.

  • Newbie
  • *
  • Messages: 8
    • Voir le profil
    • E-mail
sf::Socket::Error après quelques secondes de connexion
« le: Juillet 15, 2012, 11:43:04 am »
Bonjour,
j'ai fait un code de serveur simple utilisant le module réseau de la SFML:

#include "Serveur.h"

using namespace std;

Serveur::Serveur()
{
    log = fopen("log.txt", "w");

    fprintf(log, "Log Serveur\n************************\n\n");

    port = 4567;
    if(!socket.Bind(port))
    {
        cout << "Impossible d'ecouter le port !" << endl;
        fprintf(log, "Impossible d'écouter le port 4567\n");
    }

    ip = sf::IPAddress::GetLocalAddress();

    cout << "Votre adresse est: " << ip.ToString() << endl;

    nbreClients = 0;
    portclient = 0;

    cout << "Serveur pret !" << endl;

    fprintf(log, "Serveur prêt\n");
}

Serveur::~Serveur()
{
    fprintf(log, "Arrêt du serveur...\n");
    fclose(log);
    socket.Close();
}

std::string Serveur::getIP()
{
    return ip.ToString();
}

unsigned short Serveur::getPort()
{
    return port;
}

void Serveur::run()
{
    while(1)
    {
        switch(socket.Receive(p, sender, portclient))
        {
            case sf::Socket::Done:
            if(!clientConnecte(sender))
            {
                cout << "Nouveau client connecte: " << sender.ToString() << endl;
                fprintf(log, "Nouveau client connecte(%d): %s\n", nbreClients+1, sender.ToString().c_str());
                nbreClients = nbreClients + 1;
                clients[nbreClients-1] = sender;
            }

            broadcast(p);
            break;

            case sf::Socket::Disconnected:
            cout << "Client deconnecte: " << sender.ToString() << endl;
            break;

            case sf::Socket::Error:
            cout << "Erreur de " << sender <<  endl;
            break;

            default:
            break;
        }
        p.Clear();
    }
}

int Serveur::getClient(sf::IPAddress ipClient)
{
    int i;
    for(i = 0; i < nbreClients; i++)
    {
        if(clients[i] == ipClient)
        {
            return i;
        }
    }

    return -1;
}

bool Serveur::clientConnecte(sf::IPAddress client)
{
    int i;
    for(i = 0; i < nbreClients; i++)
    {
        if(clients[i] == client)
        {
            return true;
        }
    }

    return false;
}

void Serveur::broadcast(sf::Packet p)
{
    for(int i = 0; i < nbreClients; i++)
    {
        socket.Send(p, clients[i], 4568);
    }
}

bool Serveur::supprimerClient(int n)
{
    if(nbreClients < n+1)
    {
        return false;
    }

    else
    {
        clients[n] = clients[nbreClients-1];
        nbreClients--;
    }

    return true;
}
 

J'ai essayé de connecter deux machines du réseau local sur le serveur, tout  marche parfaitement pendant quelques temps et après environ une minute j'obtiens dans la console un message comme celui-ci:

Erreur de: 255.255.255.255
 

et plus aucun paquet n'est envoyé par le socket...
J'ai essayé  d'afficher le contenu de tous les paquets dans la console mais ils semblent tous "valides" donc je ne vois vraiment pas d'ou peut provenir cette erreur qui est pourtant systématique à chaque fois qu'il y a plus d'un client.
Lorsque seulement un client est connecté le socket ne renvoie pas d'erreur.

Quelqu'un peut il m'aider ?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::Socket::Error après quelques secondes de connexion
« Réponse #1 le: Juillet 15, 2012, 11:57:54 am »
Je ne vois rien de louche là-dedans. Te serait-il possible de mettre tout ça dans un code complet minimal (un gros main()), que je puisse le tester ? Et faire la même chose avec le code client ?

Est-ce que l'erreur se produit également lorsque les clients et le serveur tournent tous sur la même machine ?
Laurent Gomila - SFML developer

Glaucos.

  • Newbie
  • *
  • Messages: 8
    • Voir le profil
    • E-mail
Re : sf::Socket::Error après quelques secondes de connexion
« Réponse #2 le: Juillet 15, 2012, 02:28:29 pm »
Si je mettais tout dans le main ce serait:


int main()
{
    sf::SocketUDP socket;
    unsigned short port = 4567;
    if(!socket.Bind(port))
    {
        cout << "Impossible d'ecouter le port !" << endl;
    }

    sf::IPAddress ip = sf::IPAddress::GetLocalAddress();

    cout << "Votre adresse est: " << ip.ToString() << endl;

    unsigned int nbreClients = 0;
    unsigned short portclient = 0;
    sf::IPAddress clients[10];

    sf::Packet p;
    sf::IPAddress sender;

    while(1)
    {
        switch(socket.Receive(p, sender, portclient))
        {
            case sf::Socket::Done:
            for(int i = 0; i < nbreClients; i++)
            {
                if(clients[i] == sender)
                {
                    for(int i = 0; i < nbreClients; i++)
                    {
                        socket.Send(p, clients[i], 4568);
                    }
                }
            }

            cout << "Nouveau client connecte(" << nbreClients << "): " << sender.ToString() << endl;
            nbreClients = nbreClients + 1;
            clients[nbreClients-1] = sender;

            //broadcast

            for(int i = 0; i < nbreClients; i++)
            {
                socket.Send(p, clients[i], 4568);
            }
            break;

            case sf::Socket::Disconnected:
            cout << "Client deconnecte: " << sender.ToString() << endl;
            break;

            case sf::Socket::Error:
            cout << "Erreur de " << sender << endl;
            break;

            default:
            break;
        }
        p.Clear();
    }


    socket.Close();

    return 0;
}
 

mais je ne pense pas que ça vienne du serveur (directement en tous cas). En fait le client se contente de faire ceci:

un thread pour recevoir les paquets
//Thread se chargeant de la réception de paquets
void networkLoop(void* data)
{
    TankMania *tankmania = static_cast<TankMania*>(data);
    sf::SocketUDP socket;
    sf::Packet packetin;
    TankManiaPacket p;

    packetin.Clear();
    unsigned short portenvoyeur;
    sf::IPAddress ipenvoyeur;

    if(!socket.Bind(4568))
    {
        cout << "Impossible de binder le socket sur 4568" << endl;
        tankmania->writeToLog("Impossible d'écouter le port 4568");
    }

    while(run)
    {
        if(socket.Receive(packetin, ipenvoyeur, portenvoyeur) == sf::Socket::Done)
        {
            packetin >> p;
            cout << "Nouveau message: " << p.nom << " " << p.position.X << " " << p.position.Y << " " << p.position.Z << " " << p.rotation.X << " " << p.rotation.Y << " " << p.rotation.Z << "\r";

            if(p.nom != tankmania->getLocalPlayer()->getName())
            {
                tankmania->addPlayer(p.nom, true);
                tankmania->getJoueur(p.nom)->getTank()->getNode()->setPosition(p.position);
                tankmania->getJoueur(p.nom)->getTank()->getNode()->setRotation(p.rotation);
            }
        }
    }

    tankmania->writeToLog("Fin du thread réseau");
}

 

et une fonction pour les envoyer
void TankMania::sendPacketToServer(Joueur *joueur)
{
    packetout.Clear();

    TankManiaPacket p;
    p.position = joueur->getTank()->getNode()->getPosition();
    p.rotation = joueur->getTank()->getNode()->getRotation();
    p.nom = joueur->getName();

    packetout << p;

    socket.Send(packetout, ip, port);
}
 

Alors comme c'est un jeu j'appelle la fonction d'envoi de paquet à chaque tour de boucle. Quand je suis sur la même machine en client et serveur ça marche très bien mais dès que je lance le serveur sur un autre ordinateur sf::Socket::error est renvoyé au bout de quelques paquets échangés.
Finalement je me demande si ce n'est pas lié au fait que j'envoie trop de paquets (deux pcs à 60 fps) et que je ne me fais pas une sorte de déni de service ?  :P Ca ne me semble pas énorme non plus 60 paquets. J'ai testé une solution radicale qui consiste à limiter l'envoi à un paquet par seconde et étrangement ça marche très bien (en fait j'arrive à lancer le jeu sur les deux machines plusieurs minutes et les paquets transitent normalement). Mais dans la console du serveur des sf::Socket::error sont toujours affichés bien que moins nombreux. Est ce que ça viendrait uniquement d'un trop grand nombre de paquets échangés ?

Merci

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::Socket::Error après quelques secondes de connexion
« Réponse #3 le: Juillet 15, 2012, 02:48:16 pm »
Ce serait bien d'avoir le client en code minimal aussi, pour tester :)

A mon avis rien à voir avec le nombre de paquets, 60 par seconde ce n'est pas énorme et de toute façon ce qui se passerait c'est que certains paquets seraient perdus, tu n'auras pas une erreur comme ça.
Laurent Gomila - SFML developer

Glaucos.

  • Newbie
  • *
  • Messages: 8
    • Voir le profil
    • E-mail
Re : sf::Socket::Error après quelques secondes de connexion
« Réponse #4 le: Juillet 15, 2012, 08:03:28 pm »
J'ai bricolé le code minimal du client  :)


sf::SocketUDP socket; //socket global pour l'envoi
bool run = true; //threadrunning
sf::Packet pOut; //paquet d'envoi

void TankMania::sendPacketToServer()
{
    pOut.Clear();

    pOut << 125.12 << 15 << 32 << 45.1 << endl;  

    socket.Send(packetout, "localhost", 4567);
}

void networkLoop(void* data)
{
    sf::SocketUDP socket;//socket pour l'envoi
    sf::Packet packetin;

    unsigned short portenvoyeur;
    sf::IPAddress ipenvoyeur;

    if(!socket.Bind(4568))
    {
        cout << "Impossible de binder le socket sur 4568" << endl;
    }

    while(run)
    {
        if(socket.Receive(packetin, ipenvoyeur, portenvoyeur) == sf::Socket::Done)
        {
            float f1, f2, f3, f4;
            packetin >> f1 >> f2 >>f3 >>f4 ;
            cout << f1 << " " << f2 << " " << f3 << " " << f4;
        }
    }

    cout << "Fin du thread" << endl;
}

int main()
{
    sf::Thread thread(&networkLoop);

    run = true;

    thread.Launch();

    int tz = clock();

    while(1)
    {
        if(clock() - tz > 1000) //on bride l'envoi à 1paquet/sec
        {
            sendPacketToServer();
            tz = clock();
        }
    }

    run = false;

    socket.Send(pOut);

    thread.Wait();

    return 0;
}
 
« Modifié: Juillet 15, 2012, 08:18:24 pm par Glaucos. »