IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Socket et protocole UDP, ne lire que la dernière donnée reçue


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de Auteur
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    7 660
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 7 660
    Par défaut Socket et protocole UDP, ne lire que la dernière donnée reçue
    Bonjour,

    j'ai une classe qui me permet de lire les données UDP d'un PC client :
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    #include <iostream>
    #include <cstring>
    #include "SocketClass.h"
     
     
    using namespace std;
     
    SocketClass::SocketClass(int port)
    {
        this->port = port;
        this->socketError = NO_ERROR;
    	this->sec = 5;
    	this->usec = 0;
    }
     
    SocketClass::~SocketClass()
    {
    	if (socketError == NO_ERROR)
    	{
    		socketError = shutdown(socketServer, SD_BOTH);
    		socketError = closesocket(socketServer);
    	}
     
    	WSACleanup();
    }
     
    /**
    Initialisation du socket
    **/
    int SocketClass::initSocket()
    {
        WSADATA WSAData;
     
        socketError = WSAStartup(MAKEWORD(2,2), &WSAData);    // initialisation du socket, utilisation de la verions 2.0 de winsock
        if (socketError!=NO_ERROR)
        {
            return socketError;
        }
     
        // initialisation du socket
        sin.sin_addr.s_addr	= htonl(INADDR_ANY);    // pas d'adresse, mode serveur
        sin.sin_family		= AF_INET;              // protocole IPv4
        sin.sin_port		= htons(port);          // port
     
     
        // création du socket
        socketServer = socket(AF_INET, SOCK_DGRAM, 0);
     
        if (socketServer == INVALID_SOCKET)
        {
            socketError = INVALID_SOCKET;
            //WSACleanup();
            return INVALID_SOCKET;
        }
     
     
    	// bind
        socketError = bind(socketServer, (SOCKADDR *)&sin, sizeof(sin));
        if (socketError == SOCKET_ERROR)
        {
            //WSACleanup();
            return SOCKET_ERROR;
        }
     
    	// taille du buffer de lecture
        socketError = setsockopt(socketServer, SOL_SOCKET, SO_RCVBUF, (char*)&data, sizeof(data));
        if (socketError == SOCKET_ERROR)
        {
           // WSACleanup();
            return SOCKET_ERROR;
        }
     
    	socketError = NO_ERROR;
        return NO_ERROR;
    }
     
    /**
    Fermeture du socket
    **/
    int SocketClass::closeSocket()
    {
    	if (socketError == NO_ERROR)
    	{
    		socketError = shutdown(socketServer, SD_BOTH);
    		socketError = closesocket(socketServer);
    	}
     
    	WSACleanup();
     
        return socketError;
    }
     
    /**
    Time out sur le socket
    **/
    void SocketClass::setTimeOut(int sec, int usec)
    {
    	this->sec = sec;
    	this->usec = usec;
    }
     
     
    /**
    Lecture des données
    **/
    int SocketClass::readData()
    {
        int sinSize;
        int len;
     
        fd_set fd;
        timeval tv;
     
        sinSize = sizeof(sin);
        len = NO_ERROR;
     
    	if (socketError == NO_ERROR)
    	{
    		//while(1)
    		{
    			FD_ZERO(&fd);
    			FD_SET(socketServer, &fd);
     
    			tv.tv_sec = sec;		// timeout sur la méthode accept
    			tv.tv_usec = usec;
     
    			if (select(0, &fd, NULL, NULL, &tv) > 0)
    			{
    				len = recvfrom(socketServer, (char*)&data, sizeof(data), 0, (SOCKADDR *)&sin, &sinSize);
     
    				/*if (len > 0)
    					break;*/
    			   /* if (len == SOCKET_ERROR)
    					break;*/
    			}
    		}
    	}
     
    	//Sleep(20);
     
        return len;
    }

    Le client m'envoie les données en continue. Et mon serveur, lui ne lit les données avec la fonction readData() qui est appelée 1 fois par seconde. Je reçois donc plus de données que je n'en lis.

    Or il semblerait que les données soient stockées dans une pile FIFO et lors de la lecture du buffer je ne lis que la 1ère donnée reçue (la plus ancienne), or moi ce qui m'intéresse c'est la dernière donnée (la plus récente).
    J'ai pourtant précisé que la taille du buffer de lecture était de la taille de ma variable data.

    Comment je dois modifier mon code pour ne récupérer que la dernière donnée reçue et jeter le reste ?

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    J'ai surtout envie de te poser une autre question: pourquoi demander au client d'envoyer des données en continu si c'est pour te foutre royalement de la majorité des données envoyées

    Ne serait-il pas préférable (ne serait-ce que pour éviter de surcharger le réseau) de faire en sorte que le client n'envoie que des données qui devront quoi qu'il arrive être traitées par le serveur
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut
    Pour répondre à la question si pour x raison tu ne peux pas suivre les bons conseils de Koala01 :

    Si c'était FIFO (First In First Out), tu lirais justement le dernier paquet reçu en premier. Donc c'est peut-être plus FILO (une pile d'assiette) que tu voulais dire. Et si c'est le cas, tu pourrais dépiler jusqu'à lire la dernière "assiette" ?

    EDIT : oh là là mais j'arrête pas de dire des conneries moi en ce moment "first in first out ... donc le dernier d'abord..."

    Oui tu as une pile (FIFO donc ^^), et donc pourquoi tu dépile pas jusqu'à lire le dernier paquet ?

  4. #4
    Expert confirmé
    Avatar de Auteur
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    7 660
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 7 660
    Par défaut
    @koala01 : Malheureusement pour moi le client fonctionne comme ça. Mais c'est vrai l'idéal serait que le client m'envoie les données à la demande. Je vais quand même demander à la personne qui a programmé ce client de voir comment elle peut le modifier.

    @Kaamui :
    Si c'était FIFO (First In First Out), tu lirais justement le dernier paquet reçu en premier
    tu es sûr ? Si je reçois le paquet P(n) à t(n) et P(n+1) à t(n+1) c'est P(n) qui sort en premier dans une pile FIFO pas P(n+1).


    Sinon dans le cas où la modification du code client ne serait pas possible est-ce qu'il y a un moyen de procéder ? J'ai pensé à un thread qui est chargé de lire port en permanence et une fonction getData() qui me retourne une donnée à un temps t.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    FIFO = file d'attente, premier arrivé, premier servi
    LIFO = pile, dernier arrivé, premier servi
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Expert confirmé
    Avatar de Auteur
    Profil pro
    Inscrit en
    Avril 2004
    Messages
    7 660
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2004
    Messages : 7 660
    Par défaut
    J'ai créé un thread pour lire les données entrantes :
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    DWORD SocketClass::readDataFrom()
    {
    	int sinSize;
     
        fd_set fd;
        timeval tv;
     
    	static HANDLE l_hsProtect = CreateSemaphore(NULL,1,1,NULL);
    	WaitForSingleObject(l_hsProtect, INFINITE);
     
        sinSize = sizeof(sin);
        dataLen = 0;
    	dataReadLen = 0;
     
    	if (socketError == NO_ERROR)
    	{
    		while(!stopReading)
    		{
    			FD_ZERO(&fd);
    			FD_SET(socketServer, &fd);
     
    			tv.tv_sec = sec;		// timeout sur la méthode accept
    			tv.tv_usec = usec;
     
    			if (select(0, &fd, NULL, NULL, &tv) > 0)
    			{
    				dataLen = recvfrom(socketServer, (char*)&data, sizeof(data), 0, (SOCKADDR *)&sin, &sinSize);
    				if (memData)
    				{
    					dataReadLen = dataLen;
    					dataRead.id = data.id;
    					dataRead.time = data.time;
    					dataRead.x = data.x;
    					dataRead.y = data.y;
    					dataRead.z = data.z;
    					memData = false;
    				}
    			}
    		}
    	}
     
    	dataLen = 0;
    	dataReadLen = 0;
    	memData = false;
        ReleaseSemaphore(l_hsProtect,1,NULL);
     
        return 0;
    }

    L'idée est ensuite, d'utiliser un flag memData pour mémoriser la donnée à un moment voulu. Mais comment ensuite récupérer dataRead ?
    Car je vais être confronté à un problème de synchronisation. J'ai pensé aux mutex, mais je ne vois pas comment procéder.

  7. #7
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Salut toi
    Coucou tout le monde

    Bon, déjà, je précise que je ne connais rien pour ainsi dire au socket, mais... je relève toutefois, du moins je pense, ce qui me semble être des incohérences dans ton code par rapport au comportement que tu souhaites de celui-ci.

    À ce que je lis, ta variable de réception "data" n'est qu'un simple tableau de caractères, et ça ne se rapproche en rien d'un "container" ou d'une "pile", donc techniquement, à chaque lecture de celle-ci, t'es supposé avoir la dernière donnée reçue. Si tu lis tout le temps la même valeur, donc la première que tu reçois après chaque redémarrage de ton serveur, c'est que tu lis juste la première valeur stockée par ta classe, mais je crois qu'elle n'est jamais mises à jour. En d'autres termes, ça veut dire qu'après la première réception, ton serveur se ferme et n'est plus à l'écoute. Donc, outre un problème de gestion des erreurs, je pense qu'il y a une confusion et une collision entre, d'une part, le destructeur de ta classe et d'autre part, la fonction membre "closeSocket" qui en plus d'utiliser l'api "closesocket" fait aussi un WSACleanup.

    Voilà, si ça peut t'aider...

    P.-S. : si je suis à côté de la plaque, ne pas tenir compte du message
    il fallait bien que je le place mon smiley préféré

  8. #8
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par Auteur Voir le message
    Car je vais être confronté à un problème de synchronisation. J'ai pensé aux mutex, mais je ne vois pas comment procéder.
    Il faut s'assurer qu'un seul thread accède à la donnée en même temps. Schématiquement ça donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    class SocketClass {
    public:
       void readData() {
           while(!stop) {
               Data d = recvfrom(...);
               {
                    // lock le mutex pour pouvoir modifier m_lastData sans qu'un autre thread n'y accède pendant ce temps
                    std::lock_guard<std::mutex> lock(m_mutexData);
                    m_lastData = d;
                    m_hasData = true;
               }
            }
         }
     
         Data getData() {
             // lock le mutex pour pouvoir copier m_lastData sans qu'il soit modifier pendant la copie
             std::lock_guard<std::mutex> lock(m_mutexData);
             m_hasData = false; // c'est assez sale de set un attribut dans un getter
                   // mais tu vois l'idée.
             return m_lastData; // return une copie
         }
     
         bool hasData() const {
             return m_hasData;
         }
     
    private:
       Data m_lastData;
       std::mutex m_mutexData;
       std::atomic<bool> m_hasData;
    };

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Protocole UDP et Socket
    Par yazidi_marwen dans le forum Entrée/Sortie
    Réponses: 3
    Dernier message: 10/06/2012, 15h19
  2. Socket et protocole IRC
    Par EpOnYmE187 dans le forum WinDev
    Réponses: 8
    Dernier message: 10/02/2006, 14h54
  3. Ne lire que à partir du n ième caractère
    Par dj-julio dans le forum Langage
    Réponses: 2
    Dernier message: 17/01/2006, 13h21
  4. Réponses: 10
    Dernier message: 10/01/2006, 09h12
  5. raw socket et protocole TCP/IP
    Par robertmouac dans le forum Développement
    Réponses: 3
    Dernier message: 09/03/2005, 23h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo