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++

  1. #21
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    et il y a aussi les mutex anonymes si je me souviens bien... mais bon, c'est dans le principe, après, dans le genre y'a probablement plus adapté... et encore, faut voir comment c'est géré en interne... je me rappelle de sessions de "profiling" plutôt étonnantes...

    Sinon, est-ce qu'un "closesocket" serait susceptible de réinitialiser ce fameux "buffer matériel" ?

  2. #22
    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
    La fermeture de socket videra sans doute le buffer. Mais j'aimerai éviter d'enchainer les ouvertures / fermetures de socket pour récupérer 1 donnée.

  3. #23
    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,

    J'ai fait un petit break que du coup j'ai occupé à faire un client et un serveur UDP sur localhost, plutôt des prototypes on va dire. L'exercice était intéressant. Alors comme j'ai pu expérimenter la chose, j'ai une solution possible et très simple qui vise à resynchroniser la communication. Pour cela, j'ai mis en oeuvre un "timer" qui mesure le temps écoulé après l'appel bloquant "recvfrom", je précise que l'opération a lieu dans une une boucle "do-while" et qu'il n'y a pas de "thread" ou de "mutex" dans cette ébauche. Donc je disais, je mesure le temps écoulé, s'il est trop petit (éventuellement par rapport à la fréquence d'envoi du client), alors la donnée provient du buffer de la carte réseau, à l'inverse, c'est une donnée fraichement reçue.

    voilà le pseudo-code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    do {
      time start;
      recvfrom(&data);
      if (error)
        break;
      else
        time stop;
        if ((stop-start)<x_milliseconds)
          continue; //dépiler...
       do_action(data)
    } while (statement)
    J'ai pas les codes sources sous la main, mais s'il y en a qui les veulent, ou les exe des prototypes, faites-moi signe...

  4. #24
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    re,

    Une autre approche sur le même principe, mais sans timer cette fois-ci. J'ai pas fait de mesure, mais ça fonctionne.

    J'avais d'abord voulu faire du "peeking", mais ça ne donnait pas le résultat escompté. C'est une option de "recvfrom" (MSG_PEEK) mais contrairement à mon attente, l'appel reste bloquant. J'ai donc fouillé un peu msdn en ce sens à chercher quelque chose de compatible avec UDP, et j'ai trouvé un bon candidat en la fonction "ioctlsocket".

    Avec les bons paramètres, "ioctlsocket" donne la quantité de données dans la file d'attente du buffer du socket. Donc, ce que je fais, je vide le buffer jusqu'à ce qu'il n'y a plus de données en attente de traitement, ainsi, je libère le buffer du socket pour recevoir une donnée fraiche.

    Son implémentation dans le code dépendra du degré de fraicheur recherché

    Je reprendrai peut-être son expérimentation la semaine prochaine si j'ai le temps...

    En deux lignes, voilà à quoi ça ressemble en tout cas, si ça peut t'aider

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    u_long socket_pending_data= 0;
    if (::ioctlsocket(socket_file_descriptor, FIONREAD, &socket_pending_data)==NO_ERROR)
      if (socket_pending_data) // ou  if (socket_pending_data>freshness_limit)
        ...

  5. #25
    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
    Merci, je mets ça de côté jusqu'à la rentrée. Je ne connaissais pas ioctlsocket

  6. #26
    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 fait ceci mais je trouve le code moche à cause de la fonction Sleep :
    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
    void MyClass::lock()
    {
    	dataLock = false;
    	dataUpdated = false;
    	while (!dataUpdated)
    	{
    		Sleep(2);
    	}
     
    	dataLock = true;
    }
     
    void MyClass::unlock()
    {
    	dataLock = false;
    }

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    DWORD MyClass::readDataFrom()
    {
    	int sinSize;
    	int len;
    	info recvData;
     
        fd_set fd;
        timeval tv;
     
     
        sinSize = sizeof(sin);
        len = 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)
    			{
    				len = recvfrom(socketServer, (char*)&recvData, sizeof(recvData), 0, (SOCKADDR *)&sin, &sinSize);
     
    				if (!dataLock)
    				{
    					dataLen = len;
    					if (len > 0)
    					{
    						data.id = recvData.id;
    						data.time = recvData.time;
    						data.x = recvData.x;
    						data.y = recvData.y;
    						data.z = recvData.z;
    					}
    				}
    			}
    			dataUpdated = true;
    		}
    	}
     
    	dataLen = 0;
     
        return 0;
    }

  7. #27
    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 Auteur,

    Là c'est un problème de "design"... Pour les lock/unlock il faut que ça soit des opérations atomiques, mais si tu cherches un minimum de synchronicité entre tes "threads", mieux vaut jouer avec les signaux/événements... une api genre WaitForSingleObject (quelque chose comme ça, je suis pas sûr de la dénomination exacte) peut t'aider. Cependant, après vient la question du pourquoi faire un "thread" de lecture et un thread de calcul si tu peux faire tout dans un seul ?

    Une dernière chose, lorsque je me suis intéressé à ton problème l'année dernière , il m'a semblé, de souvenir, que la méthode "accept" n'était pas prise en charge par UDP, pourtant tu l'utilises...

  8. #28
    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
    La notion atomic n'existe pas sous Visual Studio 2010 car elle est apparue avec le C++11 je crois.

    Citation Envoyé par minnesota
    Cependant, après vient la question du pourquoi faire un "thread" de lecture et un thread de calcul si tu peux faire tout dans un seul ?
    Le code est intégré dans une DLL. Pour éviter de bloquer le programme principal, j'ai dû créer un thread qui ne contient que la fonction readDataFrom(). Le programme principal appelle les fonctions lock et unlock() en plus de getters (getId(), getX(), getY(), getTime()).


    Le schéma de fonctionnement du programme principal est le suivant :
    1. lock()
    2. getId(), getTime(), getX, getY(), getZ()
    3. unlock()
    4. traitement des données lues


    la méthode "accept" n'était pas prise en charge par UDP, pourtant tu l'utilises...
    il ne me semble pas voir de restrictions dans la doc :
    http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
    ou alors j'ai mal compris.

  9. #29
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Salut

    Citation Envoyé par Auteur Voir le message
    La notion atomic n'existe pas sous Visual Studio 2010 car elle est apparue avec le C++11 je crois.
    En effet !

    Je vais dévier un poil du sujet, mais puisqu'on parle de synchronisation de threads et de lecture de socket, pourquoi ne pas faire de l'asynchrone ? Comme ça plus de mutex, plus de dead-lock, meilleurs perfos. Mais je suppose que t'es trop avancé dans ton projet pour penser à une refonte maintenant, dommage.

  10. #30
    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
    Citation Envoyé par jblecanard Voir le message
    Je vais dévier un poil du sujet, mais puisqu'on parle de synchronisation de threads et de lecture de socket, pourquoi ne pas faire de l'asynchrone ?
    Intéressant comme approche. Comment procèderais-tu ? Peux-tu me donner une exemple de code ?

  11. #31
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Non atomique dans le sens très bref, qui se rapproche le plus possible d'une instruction processeur... Il ne faut pas que ta fonction contienne des "sleep" ou truc comme ça. Il faudrait peut-être même faire du "inline" mais je ne sais pas si c'est toujours d'actualité ça...

    À côté, ben c'est déjà de l'asynchrone si je ne m'abuse, sauf que pour avoir la dernière donnée envoyée comme tu le souhaites, il faut que tu te synchronises ou resynchronises un peu... Il a été proposé plusieurs méthodes qui au pire te donnent une donnée de retard, mais vu ton bout de code, je me demande si t'en as retenu une... Pour rappel, il y avait la piste du conteneur LIFO, je rajouterais que si en plus il est cyclique ça devrait être pas trop mal (je sais pas si je l'ai déjà dit ou pas), et le purgeur de la file d'attente qui te garantit une donnée fraiche.

    Pour développer un peu plus pour le conteneur cyclique, tu le remplis et tu tiens à jour un index de la position courante. Si ton conteneur est suffisamment grand, et je suppose que tout se fera sur un seul processeur ou coeur, alors la lecture d'une donnée se ferait sans risque de collision avec l'écriture et vice versa.

    Pour "accept", même si effectivement la doc MSDN est plus orientée sur TCP, du moins je trouve, il me semble que si tu gères correctement les erreurs API, tu devrais en avoir une si tu tentes d'utiliser "accept" avec UDP. À voir en tout cas... J'ai eu qu'une unique expérience sur la question, donc je ne saurais être catégorique...

  12. #32
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Salut !

    Si tu dois synchroniser deux threads pour que l'un puisse lire une donnée dans l'autre, alors tu n'es plus asynchrone.

    Citation Envoyé par Auteur Voir le message
    Le code est intégré dans une DLL. Pour éviter de bloquer le programme principal, j'ai dû créer un thread qui ne contient que la fonction readDataFrom(). Le programme principal appelle les fonctions lock et unlock() en plus de getters (getId(), getX(), getY(), getTime()).
    Mince j'avais pas vu ça. Es-tu également l'auteur de ce programme ou dois-tu fournir la DLL qui respecte l'API imposée ? Si oui, il n'y a rien que techniques asyncrhones ne puissent faire, l'usage de lock()/unlock() est intrinsèquement un mécanisme de synchronisation. J'attendrais la réponse à ma question avant de suggérer quelque chose

  13. #33
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Si tu dois synchroniser deux threads pour que l'un puisse lire une donnée dans l'autre, alors tu n'es plus asynchrone.
    Se synchroniser par rapport à la file d'attente du "buffer udp de la carte réseau" ... il faut que tu lises ou relises la discussion, au moins pour pas que tu perdes du temps dans une mauvaise direction par rapport au besoin ... Sinon, deux méthodes proposées sont bien asynchrones, threads parlant, tout au moins une, l'autre n'a pas besoin d'être "parallélisée". D'où mon étonnement dans mon précédent message de voir encore du lock/unlock, entre autres.

  14. #34
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Salut

    Citation Envoyé par minnesota Voir le message
    Se synchroniser par rapport à la file d'attente du "buffer udp de la carte réseau" ... il faut que tu lises ou relises la discussion, au moins pour pas que tu perdes du temps dans une mauvaise direction par rapport au besoin
    J'ai relu et oui j'ai bien compris Tes propositions sont recevables mais sont néanmoins synchrones, puisqu'on n'a qu'un thread et qu'il y a des appels bloquants. Il n'y pas le choix de toute façon : les sockets n'offrent pas d'interface asynchrone, on peut au mieux faire des read non bloquants mais c'est pas la panacée.

    Pour moi ici, il y a un quelque chose qui n'est pas clair, à savoir la notion de donnée "fraîche". Quand je parle d'asynchrone, et au regard de ce que j'ai compris du besoin, je pense à :
    - Un thread qui lit les données en continu tant qu'elles sont disponibles et stocke la dernière reçue. C'est lui qui se tape l'attente et les appels bloquants.
    - Un thread applicatif qui envoie une demande pour avoir la donnée (appel non bloquant) et effectue un traitement lorsqu'il la reçoit.

    Après j'ai peut-être mal compris le besoin, mais dans ce cas, une clarification d'Auteur serait la bienvenue. J'ai l'impression que ce sera synchrone par design.

    PS: Les sleep c'est mal, un chaton est tué à chaque appel.

  15. #35
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    Ah salut,

    Ben si t'as bien compris, je crois.

    Sinon pour du "read" non bloquant, quand j'avais testé vite fait, disant expérimenté, parce que j'avais encore jamais utilisé les sockets, il me semble que ça retournait tout le temps la même donnée, et quand le "buffer de la carte" était vide, ben ça redevenait bloquant. C'était une option avec MSG_PEEK, quelque chose comme ça, faut voir un message précédent, ben juste au-dessus, tiens.

    Sinon pour ton deuxième paragraphe ça a été proposé aussi. Après pour ce qui est du nombre de "threads" ça va dépendre de son interface, mais tu conviendras que les principes et les grandes lignes sont là.

    Du coup, avec le nouveau message, jblecanard, on connait toujours pas la direction prise, et pour rebondir sur le chaton, on a juste j'ai mis un "sleep" mais c'est moche



    Auteur, revient à la maison

    De toute façon, jblecanard, pour le trouver, c'est simple, soit il est dans l'espace (hAuteur), soit il est dans la taverne, soit il est dans la taverne, soit il est dans la taverne, soit il écrit un livre

  16. #36
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Ha ha !

    Citation Envoyé par minnesota Voir le message
    Sinon pour ton deuxième paragraphe ça a été proposé aussi.
    Pas tout à fait ! Ce qui est proposé, c'est:
    - Un thread qui lit les données en continu tant qu'elles sont disponibles et stocke la dernière reçue.
    - Un thread applicatif qui bloque le thread de lecture pour lire la donnée et le débloque ensuite (c'est un appel bloquant).

    S'il n'y pas besoin de performances de ouf, un simple mutex autour du getter suffit.

  17. #37
    Membre Expert
    Inscrit en
    Avril 2010
    Messages
    1 495
    Détails du profil
    Informations forums :
    Inscription : Avril 2010
    Messages : 1 495
    Par défaut
    mais il y a la variante aussi

    Citation Envoyé par minnesota Voir le message
    Pour rappel, il y avait la piste du conteneur LIFO, je rajouterais que si en plus il est cyclique ça devrait être pas trop mal (je sais pas si je l'ai déjà dit ou pas) ../.. Pour développer un peu ../.. tu le remplis et tu tiens à jour un index de la position courante. Si ton conteneur est suffisamment grand, et je suppose que tout se fera sur un seul processeur ou coeur, alors la lecture d'une donnée se ferait sans risque de collision avec l'écriture et vice versa.
    non ?

  18. #38
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Salut

    Là tu proposes le lock-free ring buffer, c'est une bonne idée mais c'est assez compliqué à coder et ça nécessite d'avoir std::atomic_flag.

  19. #39
    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 revu ma copie (en un mot j'ai réécris mon code ) car je partais dans toutes les directions et cela ressemblait de plus en plus à un code spaghetti J'ai donc appliqué la méthode de Minnesota :

    Citation Envoyé par Minnesota
    Pour développer un peu plus pour le conteneur cyclique, tu le remplis et tu tiens à jour un index de la position courante. Si ton conteneur est suffisamment grand, et je suppose que tout se fera sur un seul processeur ou coeur, alors la lecture d'une donnée se ferait sans risque de collision avec l'écriture et vice versa.

    J'ai choisi la pile. C'est à dire qu'à chaque lecture des données je rempli un tableau. Mes getter vont ensuite lire ce tableau à l'index n-1. Lorsque ce tableau est plein je remets mon compteur à 0. Le contenu du tableau est mis à jour dès qu'une donnée est disponible.

    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
     
    	int sinSize;
    	int len;
    	int id;
    	info recvData;
     
        fd_set fd;
        timeval tv;
     
        sinSize = sizeof(sin);
        len = 0;
    	id = 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)
    			{
    				len = recvfrom(socketServer, (char*)&recvData, sizeof(recvData), 0, (SOCKADDR *)&sin, &sinSize);
     
    				if (len > 0)
    				{
    					dataLen[id] = len;
    					data[id].id = id;
    					data[id].time = recvData.time;
    					data[id].x = recvData.x;
    					data[id].y = recvData.y;
    					data[id].z = recvData.z;
     
    					id = id + 1;
    					if (id > MAX)
    						id = 0;
    				}
    			}
     
    		}
    	}
     
    	for (i=0; i<MAX; i++)
    	{
    		dataLen[i] = 0;
    	}

    Ce tableau n'a pas besoin d'être grand, avec MAX = 10, cela fonctionne très bien.

    Merci à vous tous pour votre aide

  20. #40
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Attention car tu effectues un accès concurrent sur ton tableau et en particulier ton index id. L'accès à cette variable doit être protégée dans une section critique. Ca peut très bien se passer dans les tests et se casser la figure plus tard à un moment inattendu. Leproblème n'est donc pas rigoureusement résolu

    Que font les getters quand le tableau est vide ? Ils ne peuvent plus rien lire ?

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 3 PremièrePremière 123 DernièreDernière

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