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

Web & réseau Delphi Discussion :

[TClientSocket] Comment identifier les paquets reçus ?


Sujet :

Web & réseau Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 68
    Points : 45
    Points
    45
    Par défaut [TClientSocket] Comment identifier les paquets reçus ?
    Bonjour à tous

    Depuis deux ans je développe en amateur un petite application utilisée jusqu'à ce jour en dualview avec un jeu vidéo (Télémétrie en temps réel).

    Le programme grossi et devient gênant en terme de consommation des ressources (saccades dans le jeu etc...), bref, je dois passer en mode Client/Serveur; L'application principale sera démarré sur un autre PC.
    Un petit serveur léger en terme de ressource tourne en tâche de fond coté jeu-vidéo et envoi les paquets en réseau local (480 octets, 4 fois par seconde environ).

    Voilà pour la petite histoire :p
    Tout cela fonctionne très bien pour les fonctions de base, mais je ne peux pas me contenter d'envoyer systématiquement le même "Record".

    En effet, en fonction des fenêtres affichées sur l'autre PC je dois envoyer d'autres données "en plus" de celles de base.

    J'ai donc pensé mettre un identificateur de Record (mdBuffIdent dans l'exemple) en tête pour l'identifier coté client et le charger dans le "bon" Record (je ne sais pas si je suis clair :/ )

    Exemple du Record (tronqué) pour les données principales :
    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
     
    TDatasRec = record
        mdBuffIdent                   : integer;
        mdRacerSlotPilote             : integer;
        mdMotorHealth                 : single;
        mdRacerSlotCam                : integer;
        mdOilTemp                     : Double;
        mdWaterTemp                   : Double;
        mdRacerPos                    : Integer;
        mdRPM                         : Single;
        mdRPMmax                      : Single;
        mdContact                     : integer;
    ...
    ...
    ...
        mdBestLapTime                 : Single;
        mdPreviousBestLapTime         : Single;
        mdTrackSector                 : Integer;
    End;
    Est-ce que cette démarche d'identification vous semble "correcte" ?

    En fait dans mon idée je vais systématiquement vérifier le premier "integer" de chaque Buff pour savoir quelle structure Record je dois "remplir"...

    Si cela vous semble compliqué ou inutile, parce que vous connaissez un autre moyen d'y parvenir plus "professionnellement", j'aimerais bien connaitre vos avis

    Merci d'avance

    Alekhine.


    PS: Tiens, tout en écrivant ça je me rends compte que je pourrais aussi les identifier en fonction de leur volume en octets, en imaginant qu'aucun autre Record ne soit identique sur ce point...

  2. #2
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 68
    Points : 45
    Points
    45
    Par défaut
    Citation Envoyé par Alekhine Voir le message
    PS: Tiens, tout en écrivant ça je me rends compte que je pourrais aussi les identifier en fonction de leur volume en octets, en imaginant qu'aucun autre Record ne soit identique sur ce point...
    Oui carrément je suis toujours inspiré quand je passe sur votre forum


    En fait je vais, d'une part, stocker les valeurs SizeOf de chaque structures différentes dans des variables (fichier commun Client/Serveur), et, coté client, identifier la structure qui va être lu grâce à "Socket.ReceiveLength"

    Ensuite, en fonction du "volume" (en IF ou CASE je vais voir) je sais dans quelle structure je dois charger les données à lire, un petit coup de "Socket.ReceiveBuf(LaBonneStructure, SizeOf(TLaBonneStructureRec));" et l'affaire est faite

    Avec cette méthode j'échappe au transfert dans un buffer intermédiaire et je peux envoyer les paquets dans n'importe quel ordre vers le client.


    Ceci étant dit, je ne sais pas si c'est la méthode qu'emploierait un codeur digne de ce nom ^^

  3. #3
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    oui, et si tu as un buffer avec un ID sur 32 bits, tu peux même exploiter la gestion des messages de Delphi à la place du CASE ou des IF

    tu pourrais faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // pour l'envoie
     Socket.SendBuf(Size, SizeOf(Integer));
     Socket.SendBuf(Data, Size);
     
    // pour la lecture
     Socket.ReceiveBuf(Size, SizeOf(Integer));
     GetMem(Data, Size);
     Socket.ReceiveBuf(Data^,Size);
     Client.Dispatch(Data^);
     FreeMem(Data);
    petite explication sur le Dispatch

    c'est une méthode de TObject qui est utilisée notamment pour traiter les messages Windows. Cette méthode attend une structure quelconque (et pas uniquement un TMessage) dont la seule particularité est d'avoir un entier 32 bits en entête qui désigne un numéro de message (pas forcément ceux de Windows).

    Dans ton cas c'est la valeur de mdBuffIdent qui serait utilisée.

    il ne reste plus dans ton code de traiter les messages :
    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
     
    type
      TRecord1 = record
        mdBuffIdent : Integer; // Record1Ident
        ...
      end;
     
      TRecord2 = record
        mdBuffIdent : Integer; // Record2Ident
        ...
      end;
     
      TClient = class
       procedure Record1(var Data: TRecord1); message Record1Ident;
       procedure Record2(var Data: TRecord2); message Record2Ident;
       ...
      end;
    Attention, si ton TClient dérive de la VCL, je t'invite à utiliser des "Ident" supérieurs à WM_USER pour éviter les mélanges avec les messages Windows

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 68
    Points : 45
    Points
    45
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    oui, et si tu as un buffer avec un ID sur 32 bits, tu peux même exploiter la gestion des messages de Delphi à la place du CASE ou des IF

    tu pourrais faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // pour l'envoie
     Socket.SendBuf(Size, SizeOf(Integer));
     Socket.SendBuf(Data, Size);
     
    // pour la lecture
     Socket.ReceiveBuf(Size, SizeOf(Integer));
     GetMem(Data, Size);
     Socket.ReceiveBuf(Data^,Size);
     Client.Dispatch(Data^);
     FreeMem(Data);
    petite explication sur le Dispatch

    c'est une méthode de TObject qui est utilisée notamment pour traiter les messages Windows. Cette méthode attend une structure quelconque (et pas uniquement un TMessage) dont la seule particularité est d'avoir un entier 32 bits en entête qui désigne un numéro de message (pas forcément ceux de Windows).

    Dans ton cas c'est la valeur de mdBuffIdent qui serait utilisée.

    il ne reste plus dans ton code de traiter les messages :
    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
     
    type
      TRecord1 = record
        mdBuffIdent : Integer; // Record1Ident
        ...
      end;
     
      TRecord2 = record
        mdBuffIdent : Integer; // Record2Ident
        ...
      end;
     
      TClient = class
       procedure Record1(var Data: TRecord1); message Record1Ident;
       procedure Record2(var Data: TRecord2); message Record2Ident;
       ...
      end;
    Attention, si ton TClient dérive de la VCL, je t'invite à utiliser des "Ident" supérieurs à WM_USER pour éviter les mélanges avec les messages Windows

    Bonsoir,

    Hmmmm oui effectivement c'est une très bonne idée ça, merci , d'autant que par la suite je dois communiquer au client certaines touches tapées au clavier, pour éviter que le joueur soit obligé d'accéder au clavier de l'autre PC.
    Je pourrais donc m'inspirer de ce principe.

    Par contre ce que j'ai pondu concernant la distinction entre les volumes en octets des structures envoyés ne fonctionne pas :/

    Ce soir je test le premier exemple concret :
    J'ai une strcuture REC qui est envoyée en boucle au client, parce que ce sont des données constament renouvellées (comme les températures et la vitesse, etc...).

    Là je vient d'essayer d'envoyer une structure qu'on a besoin qu'une fois du coté client (le nom du pilote, la longueur de la piste chargée, et d'autre données qui ne changeront pas avant la sortie de cette scéance de course).
    C'est un timer OneShot qui envoi ce paquet.

    Malheureusement ça ne se passe pas comme je le croyais :/
    J'avais imaginé pouvoir intercaller le REC OneShot entre deux REC constant...
    Le REC constament envoyé fait 468 octets, la structure OneShot fait 200 octets.

    Et bien au moment ou je send la 2eme structure j'ai 468+200 octets à lire coté client lol, je pensais voir arriver les 200 séparément, pour justement les identifier. Voir le bout de code ci-dessous.

    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
     
    procedure TFrmGTREMotecAdd.ClientSocket1Read(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      Ident : integer;
    begin
      Ident := Socket.ReceiveLength;
      WLNetwork.caption := IntToStr(Ident);
      if Ident = IdentMydata then begin
        Socket.ReceiveBuf(myData, SizeOf(myData));
      end
      else
      if Ident = IdentOneShotData then begin
        Socket.ReceiveBuf(OneShotData, SizeOf(OneShotData));
        beep;
      end;
      RefreshMotecAdd;
    end;

    le "WLNetwork.caption := IntToStr(Ident);" c'est le mouchard qui m'a fait comprendre pourquoi ça ne fonctionnait pas :s
    Pire : Alors que la structure record supplémentaire n'est envoyé qu'une fois (mais jamais lu...), le mouchard me montre toujours 668 octets à lire.
    Du coup je ne lis plus rien puisque ce "volume" identifiable ne correspond à rien dans mon code :/

    Je suis bon pour penser autrement et passer un coup de karsher dans le code

    D'ailleurs même avec un buffer tampon je n'arriverais pas à m'en sortir à cause de ce "cumul". Parce que là j'en suis à en envoyer 2, mais en fait, en fonction des choix dynamiques fait par le pilote sur le client (différentes fenêtres affichées ou non), je vais être amené à en envoyer 6 ou 7 différentes.

    Je le sens bien chaud ce système :/


    [EDIT]
    Après relecture de l'aide sur TCustomWinSocket.SendBuf je comprends mieux pourquoi ça ne peut pas fonctionner : La DLL WinSock a ses propres tampons... ça avale temps que ça peut (sinon -1 ), donc à l'autre bout je suis chocolat avec le cumul

  5. #5
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 807
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 807
    Points : 13 503
    Points
    13 503
    Par défaut
    Code le premier word de ton message comme étant la taille (ou l'identifiant) de la donnée.

    A la réception, tu ne commences par lire que le 1er word (2 octets) qui te donne ton Ident.

    La suite est comme tu l'as déjà codée.

    Le tout étant inclus dans une boucle pour vider le buffer.

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    68
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 68
    Points : 45
    Points
    45
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    Code le premier word de ton message comme étant la taille (ou l'identifiant) de la donnée.

    A la réception, tu ne commences par lire que le 1er word (2 octets) qui te donne ton Ident.

    La suite est comme tu l'as déjà codée.

    Le tout étant inclus dans une boucle pour vider le buffer.
    Magnifique ! Merci !


    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
     
      TDatasRec = record
        Size                          : Integer;
        SynchroPTR                    : integer;
        mdRacerSlotPilote             : integer;
        mdRacerSlotCam                : integer;
        mdRacerPos                    : Integer;
    ...
    ...
    ...
        mdContact                     : integer;
        mdmyTotalLap                  : Integer;
        mdGear                        : Integer;
        mdTrackSector                 : Integer;
        mdCarBrakeLight               : Integer;
        mdCarLight                    : Integer;
        mdPitReq                      : integer;
      end;
     
     
    TOneShotRec = record
      Size                            : Integer;
      SynchroPTR                      : integer;
      osLenTrack                      : single;
      osCarMasse                      : single;
    ...
    ...
    ...
      osTireRadiusFront               : Double;
      osmdOnlineRacerNameStr          : String20;
    end;
     
    Var
      IdentMydata                     : integer = SizeOf(TDatasRec);
      IdentOneShotData                : integer = SizeOf(TOneShotRec);


    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
     
    procedure TFrmGTREMotecAdd.ClientSocket1Read(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      Ident, Next, Gap : integer;
     
    begin
      Next := Socket.ReceiveLength; // Taille totale à lire
      if Next = 0 then exit;        // Va comprende porquoi Onread est déclenché avec 0 à lire !?
     
      Gap  := SizeOf(myData.Size);  // Taille de la variable d'identification des paquets (identique en tête de chaques structures)
     
      {$IFDEF SRVDEBUG}
      WLNetwork.caption := IntToStr(Next); // Mouchard
      {$ENDIF}
     
      REPEAT
        Socket.ReceiveBuf(Ident, Gap);
        if Ident = IdentMydata then begin // l'entête de paquet concerne la structure myData
          // .SynchroPTR Suis directement .Size dans toutes les différentes structures Record
          // On doit commencer à charger les données après myData.Size
          // -Gap Parce qu'on a déjà lu les premiers octets...
          Socket.ReceiveBuf(myData.SynchroPTR, SizeOf(TDatasRec)-Gap);
          if Next = Ident then BREAK
          else Next := Next - Ident; // Next détermine si on doit encore lire
        end
        else
        if Ident = IdentOneShotData then begin // l'entête de paquet concerne la structure OneShotData
          Socket.ReceiveBuf(OneShotData.SynchroPTR, SizeOf(OneShotData)-Gap);
          if Next = Ident then BREAK
          else Next := Next - Ident;
        end
        else begin beep; BREAK; end;  // sécu de debug, les beep indiqueront à l'utilisateur qu'il a un ScanTime trop rapide (engorgement)
      UNTIL Next = 0;
     
      RefreshMotecAdd;
    end;

    Attention au piège : l'évènement OnRead survient parfois (souvent même !) avec 0 à lire !? je pige pas trop là mais bon...

    Sinon c'est du tonerre, je vais pouvoir envoyer les autres structures Record et terminer ce programme !


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

Discussions similaires

  1. GCOV: Comment identifier les branches?
    Par Alain38 dans le forum Linux
    Réponses: 1
    Dernier message: 22/02/2009, 13h42
  2. comment identifier les different SQLexception
    Par muslim1987 dans le forum JDBC
    Réponses: 1
    Dernier message: 02/07/2008, 09h15
  3. comment intercepter les paquets reseau
    Par hamiding10 dans le forum Servlets/JSP
    Réponses: 1
    Dernier message: 02/04/2008, 15h57
  4. [W3C] Comment identifier les "successfull controls" à la soumission d'une form?
    Par Spout dans le forum Balisage (X)HTML et validation W3C
    Réponses: 4
    Dernier message: 11/02/2008, 23h28
  5. Réponses: 1
    Dernier message: 17/06/2005, 10h35

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