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

x86 32-bits / 64-bits Assembleur Discussion :

shmget ou pipe ?


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut shmget ou pipe ?
    Salut,

    J'aimerais savoir quelle est la meilleure méthode entre deux lorsque les deux peuvent fonctionner ?
    Meilleure en niveau performance, et aussi fiabilité..

    Niveau performance, je ne compte pas faire un test de performance, car avec du multithread je ne pense pas que ce serait fiable.
    Mais à voir comme ça je dirais que la méthode pipe sera moins performance, de part le simple faite que je sois obligé de coder plusieurs "write" et "read".
    En tout cas j'ai été agréablement surpris par ce systême. Tellement simple et efficace.

    Pour shmget, pas 50 "write" et "read" à coder, donc ça allège pas mal le code.
    En dehors de ça, il faut que je termine ma deuxième version de code avec cette méthode pour voir concretement ce que ça donne, mais pour l'instant ça fonctionne bien.

    En faite, ce que je ne voudrais pas, c'est finir complètement mon code (environ 1000 lignes lorsqu'il le sera) avec l'une ou l'autre méthode, et au final m'apercevoir qu'il y a des bugs dans certaines conditions.
    Pour ça que j'aimerais connaître la méthode la plus fiable, même si les bugs peuvent être possible pour les deux.

    Le multhreading, c'est nouveau pour moi, et donc encore assez nébuleux.
    Là je m'y suis mis simplement par obligation pour ce code. Il s'agit de ma pile tcp que j'avais commencé (en style statique) et que je dois finir en style dynamique..
    En gros ça donne ça;

    - Thread n°1: gestion réception et envoie des segments tcp, et envoie de la partie "tcp-data" des segments réceptionnés au second thread, qui en fait ce qu'il veut.

    - Thread n°2: Fonctionne comme n'importe quel programme qui contient une partie réseau avec l'api "recv" ou "send" en mode "sock_stream". Donc il envoit également la partie "tcp-data" au thread n°1 qui s'occupe de la placer dans un segment tcp pour l'envoyer sur le réseau.

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Finalement j'ai opté pour shmget.
    J'ai pu réduire gravement la taille du code.
    Et c'est beaucoup plus lisible.
    Juste un peu galère au niveau de l'écriture du code, puisqu'il faut mettre des addresses de mémoire dans des buffers, et donc ne pas confondre entre les adresses des buffers et les adresses de la mémoire.
    En dehors de ça, c'est tout de même beaucoup plus clair.
    Je pourrais aussi re-déplacer de la mémoire partagé vers des buffers de la mémoire du thread en cours, ça simplifirait l'écriture du code par la suite, mais bon, c'est faire une chose inutile juste pour simplifier le coding et j'aime pas ça.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Re-finalement, shmget est inutilisable dans mon cas.
    Il fait surchauffer le proc à mort. Toujours un core à 100%
    Heureusement que je m'en suis aperçu avant d'aller plus loin.

    J'ai testé avec mon code version pipe, aucun problème, le proc ne bouge pas d'un millimètre.

    Et c'est vraiment la seule est unique différence entre les deux codes.
    Alors après je ne sais pas si c'est un bug quelconque, ou si il ne faut pas utiliser la shmem dans des boucles. Mais si c'est les boucles le problème, alors c'est mort car je ne peux pas faire de transfert shmem hors de ces boucles. Mon code fait que c'est impossible. Je pourrais le faire uniquement après les boucles, donc aucun intérêt.

    J'ai même tenté des nanosleep, mais c'est impossible.
    100 ms, ça ne reçoit plus rien, logique
    10 ms ça reçoit avec un énorme décalage, et le proc remonte à 100%.

    edit: C'est forcément les boucles, puisque le proc ne bouge plus avec un nanosleep à 100ms. Boucles qui sont identiques avec les pipes, et en mode non-bloquant, avec beaucoup plus de ligne de code (pour les "read et "write"). Donc c'est forcément la shmem dans les boucles qui cause ce problème.

    Bref ça fonctionne niquel avec les pipes, donc je vais revenir à ça, même si c'est lourd à coder.

    Galère quand même tout ça.
    Depuis un moment je commence à comprendre les gens qui font leur propre OS.
    C'est the ultime solution pour être tranquille.
    Car on est jamais mieux servi que par sois-même ^

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Hello,

    Citation Envoyé par n5Rzn1D9dC Voir le message
    Re-finalement, shmget est inutilisable dans mon cas.
    Il fait surchauffer le proc à mort. Toujours un core à 100%
    Heureusement que je m'en suis aperçu avant d'aller plus loin.
    Tu travailles avec quel système d'exploitation ?

    « shmget » signifie « Get a segment of SHared Memory ». Il est certain que la mémoire partagée est de loin la méthode la plus efficace — en local sur une machine donnée — puisque le système d'exploitation n'a absolument rien à faire une fois le segment alloué.

    Par contre, un tube est « bloquant », c'est-à-dire que c'est le système d'exploitation qui garde la main tant qu'il n'y a rien à lire dedans. Et comme c'est lui aussi qui gère les stats du processeur et qui, éventuellement, contrôle en conséquence l'échelonnement en fréquence, il va forcément faire cela au mieux si tu lui laisses la main. Si ton CPU tourne à 100 %, c'est parce que tu fais une attente active.

    Si tu travailles avec les IPC SysV, le mécanisme par excellence pour gérer les accès concurrents est les sémaphores : semget().

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Salut,

    Je suis sous Debian testing.

    Oui j'avais pensé à tester les sémaphores, ça fait plusieurs fois que je vois des commentaires positifs sur ça, mais le problème c'est qu'il y a deux boucles:
    - Dans le thread 1 pour gérer la réception des segments tcp via un "read"
    - Dans le thread 2 pour contrôler la partie "tcp-data" envoyé par le thread 1.

    Mais pour celle du thread 1, il n'y a pas de shmem dans la boucle, c'est ça qui est étrange..
    Du coup je m'étais dit que ça ne changerait rien étant donné que le problème est lié aux deux boucles.. (ça ne le fait plus avec un nanosleep de 100ms sur les deux boucles).

    Par contre, aucun problème avec les pipes, même pour celle du thread 1, qui est non-bloquante.
    J'ai fait plusieurs tests avec les pipes, et apparemment (en tout cas sur mon systême), il n'y a que les "read" coté parent qui sont bloquant si vide. Et mon thread 1 est enfant.

    edit: c'est un code avec fork.

    Pour ça que je n'ai pas trop compris.
    Pour la boucle du thread 1, il n'a pas d'accès shmem, donc pourquoi le proc s'affole ? Aucune idée.

    J'avais aussi pensé à faire un "pause" / "sigaction" avant de me rendre compte tout fonctionnait parfait avec le code pipe.
    Je n'ai pas besoin de faire de portabilité, c'est un code juste pour moi, donc je me demandais si ce ne serait pas suffisant plutôt que d'utiliser une sémaphore.

    Je vais re-testé quand même voir si je n'ai raté quelque chose.

  6. #6
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par n5Rzn1D9dC Voir le message
    Re-finalement, shmget est inutilisable dans mon cas.
    Il fait surchauffer le proc à mort. Toujours un core à 100%
    Heureusement que je m'en suis aperçu avant d'aller plus loin.

    J'ai testé avec mon code version pipe, aucun problème, le proc ne bouge pas d'un millimètre.
    Comme dit plus haut, je ne pense pas que ce soit dû en soi à shmget(). Quand tu utilises des pipes, ils sont bloquants, donc la question ne se pose pas. Dès que tu passes à de la mémoire partagée, il n'y a plus rien pour bloquer ta boucle en soi, donc elle tourne en permanence. Il faut donc passer la main au système quand il n'y a rien à faire et la récupérer au bon moment. Il s'agit donc de conception en amont, et de l'utilisation de l'outil approprié en conséquence.

    Bref ça fonctionne niquel avec les pipes, donc je vais revenir à ça, même si c'est lourd à coder.
    Si tes données sont courtes, c'est effectivement ce qu'il y a de mieux puisqu'on peut transmettre l'info en même temps qu'on débloque le processus. Si elles sont longues, en revanche, il vaut mieux utiliser un pipe ou un sémaphore uniquement pour débloquer le processus et laisser celui-ci chercher ce dont il a besoin dans la mémoire partagée.

    Depuis un moment je commence à comprendre les gens qui font leur propre OS.
    C'est the ultime solution pour être tranquille.
    Car on est jamais mieux servi que par sois-même ^
    Mouais. Créer son propre OS est très stimulant, c'est certain, mais l'emmener à terme et le maintenir ensuite devient très rébarbatif. Personnellement, j'ai arrêté de penser à cela quand j'ai commencé à toucher à UNIX. D'abord parce que je me suis rendu compte que tout ce à quoi j'avais pu penser sur le plan structurel existait déjà dans ce système, y compris des choses que je n'aurais jamais osé espérer sous D.O.S. ou Windows, comme les liens durs du système de fichiers.

    Avec cela, comme c'est un système qui existe depuis l'aube de l'informatique, ce qui est important pour moi qui suis de la génération 1980, que c'est extrêmement stable, que ça a été lancé par des pontes de l'informatique théorique comme Denis Ritchie, Ken Thompson et Kernigan, que ce sont les mêmes qui ont lancé le langage C, que même MacOS converge vers ça aujourd'hui et que, cerise sur le gâteau, les distribs de Linux et de BSD sont gratuites, communautaires, collaboratives et complètement open source, j'ai vite été convaincu du fait que s'il faut s'investir dans un framework, c'est celui-ci.

    C'est toujours intéressant de visiter osdev et autres recueils du genre pour partir zéro, bien sûr. Mais de temps en temps, j'aimerais bien que ce soit sur des architectures autres que celle du PC (qui avec cela est probablement l'une des plus mauvaises jamais conçues). L'époque des huit et seize bits me manque pour cela.

    Citation Envoyé par n5Rzn1D9dC Voir le message
    Oui j'avais pensé à tester les sémaphores, ça fait plusieurs fois que je vois des commentaires positifs sur ça, mais le problème c'est qu'il y a deux boucles:
    - Dans le thread 1 pour gérer la réception des segments tcp via un "read"
    - Dans le thread 2 pour contrôler la partie "tcp-data" envoyé par le thread 1.
    Je ne sais pas si tu le sais déjà ou pas mais si c'est cela qui t'ennuie, il existe select() pour surveiller plusieurs descripteurs à la fois dans un même fil.


    Je n'ai pas besoin de faire de portabilité, c'est un code juste pour moi, donc je me demandais si ce ne serait pas suffisant plutôt que d'utiliser une sémaphore.
    Un sémaphore. « Sémaphore » est masculin. ;-)

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Comme dit plus haut, je ne pense pas que ce soit dû en soi à shmget(). Quand tu utilises des pipes, ils sont bloquants, donc la question ne se pose pas. Dès que tu passes à de la mémoire partagée, il n'y a plus rien pour bloquer ta boucle en soi, donc elle tourne en permanence. Il faut donc passer la main au système quand il n'y a rien à faire et la récupérer au bon moment. Il s'agit donc de conception en amont, et de l'utilisation de l'outil approprié en conséquence.
    Mais normalement ce n'est pas bloquant dans mon cas.
    J'ai fait plusieurs test justement pour être certain avant de commencer, il n'y a que les "read" coté parent qui sont bloquant. Et le thread 1 est enfant.
    Par contre le thread 2 est parent, donc la boucle de "read" pour les "tcp-data" est bloquante, ou du moins l'était puisque j'ai utilisé pipe2 +o_nonblock.

    Là je suis un peu largué. J'ai déjà lu le man de select oui, mais jamais utilisé.
    Il y a tellement de possibilité de faire ce que je veux faire. Je vais devoir tester plusieurs choses.
    Le problème c'est les structures. Là je voulais tester les sémaphores en premier, mais bam, des méga-structures super lourdes. Donc ça va me prendre un moment le temps de retrouver les éléments dans les headers et réécrire tout ça.
    Idem pour pause ou signal.
    Idem pour signalfd, qui a l'air pas mal d'ailleurs.

    Le faite de ne pas avoir de debugger en gui me ralentie énormément.
    Je ne comprendrais jamais les mecs qui ont les connaissances et le temps pour faire un bon debugger, et qui ne font soit:
    - Pas de gui.
    - Une mauvaise gui.

    C'est pourtant la partie la plus simple et la moins longue.
    Edb par exemple est atroce.. La ligne en cours n'est pas surligné, il y a juste une petite flèche, c'est super fatiguant pour les yeux et la tête de toujours devoir chercher la ligne en cours.
    Je ne comprend pas ce qu'il leur passe par la tête quand ils font (ou ne font pas) leur gui.
    C'est tellement agréable de travailler sous imm debugger. La gui est parfaite, avec un fond noir par défaut. Lui il a vraiment assuré. J'ai progressé énormément grace à ce debugger. Je peux y passer des heures juste par plaisir.
    Edb j'y reste 10 minutes max, quand j'ai pas le choix car c'est le seul en gui x64.

    En faite, faire un OS m'interesserait surtout pour pouvoir faire ce que je veux comme je le veux. Sans limitation ou blocage par rapport à tel ou tel truc qui à besoin de tel ou tel autre truc pour fonctionner.
    Au début ça allait, mais là je commence à partir dans des trucs qui font que ça devient un peu galère.
    Il doit y avoir moyen de gérer les threads simplement et proprement quand on a son propre OS.
    C'est juste ça qui me dérange, coder en asm dans un environnement C.
    Mais en dehors de ça, je suis bien sous Linux et j'y resterais.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    J'ai relus le man de select, c'est vrais que ça pourrait le faire.
    Je vais tester.

    Si tes données sont courtes, c'est effectivement ce qu'il y a de mieux puisqu'on peut transmettre l'info en même temps qu'on débloque le processus. Si elles sont longues, en revanche, il vaut mieux utiliser un pipe ou un sémaphore uniquement pour débloquer le processus et laisser celui-ci chercher ce dont il a besoin dans la mémoire partagée.
    Tout dépend ce que tu appels courtes.
    Ca ne dépassera jamais la taille d'un segment tcp / read.
    En tout cas ça va très vite, puisqu'un nanosleep de 10ms suffit à provoquer un gros décalage dans la réception.

    Je vais tester tout ça. J'ai encore beaucoup de travail de toute façon, c'est plus compliqué que je le pensais. Mon systême envoie les données trop rapidement..
    C'est quelque chose que je n'avais pas envisagé.
    Je me sers d'un serveur smtp pour tester.
    Si le serveur envoie plusieurs réponses "250" au "ehlo client"' avec un message descriptif, la boucle passe dès qu'elle recoit le premier, ce qui envoie le "mail from" alors que les autres "250" liés au "ehlo client" sont encore en train d'arriver, lol !

    Il n'y a pas ce problème avec recv.. La boucle passe après le premier "250", comme pour mon code, sauf qu'à l'appel suivant de recv, pour récupérer ce qui est lié au "mail from", je reçois bien la réponse du "mail from", et non les restes du précédant "250" lié au "ehlo client".

    Pour ça aussi, je bloque un peu.
    Ce serait pourtant très simple en http avec du chunked ou content-lenght, mais pas pour du smtp, puisque la réponse est "250" pour:
    - ehlo client
    - mail from
    - rcp to

    Et ce qui vient "après" le 250, ça change selon les serveurs il me semble.. Donc il n'est possible que de se baser sur le smtpcode, sans la suite.
    Donc soit mon code va trop vite, soit j'ai zappé un truc important pour gérer ça.
    Mais par exemple, pour le serveur smtp que j'utilise pour mes tests, il envoie deux messages 250 pour le ehlo client.
    Comment prédire qu'il y en aura un de plus ? C'est impossible ?

    > = client
    < = serveur

    psh+ack > ehlo client
    ack <
    psh+ack < 250 du ehlo (1)
    ack >
    psh+ack < 250 du ehlo (2)
    ack >
    psh+ack > mail from
    ack <
    psh+ack < 250 du mail from
    ack >

    etc ...

    Sauf que, dans mon code, ça donne ceci:

    psh+ack > ehlo client
    ack <
    psh+ack < 250 du ehlo (1)
    ack >
    psh+ack > mail from
    psh+ack < 250 du ehlo (2)
    ack >
    ack <
    psh+ack < 250 du mail from
    ack >

    Après je sais qu'ils ont prévu le cas dans la rfc pour les serveurs trop lent, via le timestamping, mais je ne suis même pas sur que ce soit mon code qui est trop rapide, ou simplement que j'ai oublié un truc.

    Pas simple tout ça finalement, mais amusant.

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Finalement mon code fonctionne parfaitement.
    Je me suis embrouillé tout seul.
    J'ai ressortie le code d'un vieux client smtp que j'avais codé, et ça fait exactement pareil.. Mais ça fonctionne..

    220-server (acceuil)
    250-server (ehlo 1er push+ack)
    250-8BITMIME (ehlo 2ème push+ack)
    250 XFILTERED (ehlo 2ème push+ack)
    250 2.1.0 Ok (rcpt from)
    250 ok (data)
    354 Start mail input; end with <CRLF>.<CRLF>
    Normalement il y a deux réponses "250 2.1.0 Ok":
    - Celle pour "mail from"
    - Celle pour "rcpt to"

    Et je reçois uniquement celle de "rcpt to", à cause des deux codes "250" pour le "ehlo" qui causent problème.
    Je n'avais jamais fait attention vu que ça fonctionnait parfaitement.
    Il doit y avoir moyen de coder une boucle pour éviter ce problème mais là toute de suite je ne vois pas.

    Il y a juste un truc qui pose problème dans mon code de gestion des segments tcp.
    La "windows size".
    J'avais mal compris sont intérêt.
    Ca permet de connaître le nombre de byte avant de devoir envoyer un ack.
    Moi je calculais et envoyait le ack après chaque push+ack.
    Je n'avais pas remarqué le problème jusqu'à présent, mais là en faite ce qu'il se passe c'est que mon code envoit le "mail from" avant le ack du 2ème "250" du "ehlo".
    Ce qui fait que le serveur me renvoit ce 2ème "250", pour une raison obscure pour le moment, car le ack est parfaitement calculé.

    Le code avec l'api "recv" envoie également le "mail from" avant le 2ème "250" du "ehlo".
    La seule différence c'est qu'elle se sert de la windows size et donc envoit le ack au dernier moment. Donc plusieurs push+ack sont fait entre le client et le serveur sans échange de ack.

    Je vais m'en servir, normalement ça devrait résoudre le problème puisque c'est la seule différence entre les deux captures réseaux.

    a+

  10. #10
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par n5Rzn1D9dC Voir le message
    J'Tout dépend ce que tu appels courtes.
    Ca ne dépassera jamais la taille d'un segment tcp / read.
    Un pipe est un descripteur de fichier en écriture qui débouche immédiatement sur un autre descripteur en lecture, sans passer par le disque. Ce tube est doté d'un tampon minimum dont tu peux obtenir la taille avec ulimit -p (en blocs de 512 octets). Généralement, la taille de ce tube est fixé à 4096 octets. Un segment TCP tourne souvent entre 1500 octets typiques et 65000 pour les jumbos. Donc, difficile de trancher.

    En tout cas ça va très vite, puisqu'un nanosleep de 10ms suffit à provoquer un gros décalage dans la réception.
    Oui mais à part le fait que c'est géré par le noyau, ce n'est pas très différent d'un bête buffer circulaire que tu gérerais dans ton propre programme. L'ennui, c'est que ça t'oblige quand même à franchir la barrière du mode noyau à chaque fois (principe de l'appel système) et d'ajouter pas mal d'overhead dans sa gestion, sans compter que tu préemptes le processus à chaque fois.

    Mon systême envoie les données trop rapidement..
    C'est quelque chose que je n'avais pas envisagé.
    C'est le principe de toute file d'attente et ça a été l'objet de nombreuses études, notamment par Dijkstra. Il y a les histoires de deadlock, les « famines processus », etc. Mais l'idée générale reste que cela ne sert à rien de lancer deux processus en parallèle si le premier doit attendre que le second ait fini pour continuer son propre travail.

    Je me sers d'un serveur smtp pour tester. Si le serveur envoie plusieurs réponses "250" au "ehlo client"' avec un message descriptif, la boucle passe dès qu'elle recoit le premier, ce qui envoie le "mail from" alors que les autres "250" liés au "ehlo client" sont encore en train d'arriver, lol !
    D'une manière générale, il ne faut jamais se fier à la validité de ce que va t'envoyer un processus distinct, que ce soit à travers le réseau ou même sur la même machine mais depuis un autre programme. Quand on écrit des clients et serveurs, il faut s'efforcer de les rendre extrêmement résilients. Il se peut que ton client t'envoie volontairement des données pourries pour essayer de faire planter ton serveur, il se peut également qu'il ne soit pas à jour, il se peut qu'un parasite pollue les données envoyées et, enfin, il est tout-à-fait possible que le client « raccroche » avant la fin, soit à l'initiative de l'utilisateur, soit parce qu'il a planté ou que la liaison entre les deux s'est effondrée.

    L'approche la plus naturelle et la plus répandue consiste à mettre en place un automate à états et une boucle principale. Ton automate contient une liste d'états prédéfinis du style « phase 1 », « phase 2 », « phase 3 », etc. et se trouve toujours dans un seul d'entre eux. À chaque tour de boucle, tu lis une seule ligne. Tu vérifies alors si elle est valide dans la forme, et si elle a du sens en fonction du contexte (autrement dit, de l'état dans lequel tu te trouves). Tu décides alors de changer — ou non — d'état en fonction de ce qui t'as été passé et tu refais un tour de boucle pour lire la suivante.

    Le protocole SMTP a justement été conçu pour rendre très simple ce genre de traitement, puisqu'il n'y a pour ainsi dire aucune grammaire, obligeant à mettre en place un système de parsing, comme il faudrait le faire pour traiter une requête SQL par exemple. Chaque ligne est une instruction, et le code initial permet de la traiter très facilement.

    Il n'y a pas ce problème avec recv.. La boucle passe après le premier "250", comme pour mon code, sauf qu'à l'appel suivant de recv, pour récupérer ce qui est lié au "mail from", je reçois bien la réponse du "mail from", et non les restes du précédant "250" lié au "ehlo client".
    Pour ça aussi, je bloque un peu.
    Ce serait pourtant très simple en http avec du chunked ou content-lenght, mais pas pour du smtp, puisque la réponse est "250" pour:
    - ehlo client
    - mail from
    - rcp to
    C'est-à-dire que « 250 », ça veut dire « OK », tout simplement. C'est donc le feu vert qui te permet de passer à l'étape suivante.

    Et ce qui vient "après" le 250, ça change selon les serveurs il me semble.. Donc il n'est possible que de se baser sur le smtpcode, sans la suite.
    Au départ, si. Normalement, les message texte qui suivent sont purement informatifs et permettent à l'utilisateur de voir immédiatement de quoi il s'agit, mais il ne sont pas censés avoir de sémantique au niveau de l'automate. D'ailleurs, certains serveurs de courrier se permettent certaines largesses avec les messages renvoyés, en étant parfois assez familiers dans leur langage. :-)

    Maintenant, ces messages peuvent effectivement contenir une information supplémentaire qu'il faut savoir parser, comme par exemple le domaine d'authentification que l'on passe à HELO ou EHLO, et la liste des formats pris en charge transmis juste après. Seulement, ceux-ci dépendent du contexte, encore une fois, et sont documentés par le protocole.

    Ça veut dire que ça ne change rien au fonctionnement initial de la chose : c'est d'abord le code qui fait foi, puis vient une analyse éventuelle du reste de la ligne si le contexte s'y prête.

    Donc soit mon code va trop vite, soit j'ai zappé un truc important pour gérer ça.
    Mais par exemple, pour le serveur smtp que j'utilise pour mes tests, il envoie deux messages 250 pour le ehlo client. Comment prédire qu'il y en aura un de plus ? C'est impossible ?
    Effectivement ! Tout ceci n'est valable qu'à partir du moment où l'on renvoie des réponses sur une seule ligne à la fois. On peut en renvoyer plusieurs dans la mesure où il est prévu à l'avance que ce soit le cas.

    Lorsque l'on fait le test à la main, on ne se pose pas la question car on voit rapidement si le serveur a fini ou non de nous transmettre des informations et si celles-ci ont l'air complètes ou pas. Il est évident que ce n'est pas possible au niveau d'un programme, surtout quand les codes de retour (le 250) sont les mêmes d'un ordre à l'autre.

    Maintenant que l'on a cerné le problème, il faut essayer d'en trouver l'origine. Et il y a des chances que cela date de l'introduction de EHLO en particulier. Il n'y a plus qu'à compulser la RFC 5321 pour essayer de trouver l'info qui nous intéresse. Et caché au milieu du document, on trouve un paragraphe laconique :

    Citation Envoyé par RFC 5321
    Normally, the response to EHLO will be a multiline reply. Each line
    of the response contains a keyword and, optionally, one or more
    parameters. Following the normal syntax for multiline replies, these
    keywords follow the code (250) and a hyphen for all but the last
    line, and the code and a space for the last line. The syntax for a
    positive response, using the ABNF notation and terminal symbols of
    RFC 5234 [7], is:

    ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
    / ( "250-" Domain [ SP ehlo-greet ] CRLF
    *( "250-" ehlo-line CRLF )
    "250" SP ehlo-line CRLF )

    Et voilà ! La RFC spécifie que lorsqu'on réponse tient sur plusieurs lignes, le code est immédiatement suivi d'un « - », qu'on retrouve donc sur toutes les lignes sauf la dernière.

    C'est un peu l'équivalent du « \ » en fin de ligne du langage C et assimilé. Cette réponse est assimilée à une seule et unique grande ligne et le code qui redémarre après chaque retour est censé être le même.

    Après je sais qu'ils ont prévu le cas dans la rfc pour les serveurs trop lent, via le timestamping, mais je ne suis même pas sur que ce soit mon code qui est trop rapide, ou simplement que j'ai oublié un truc.

    Pas simple tout ça finalement, mais amusant.
    C'est le piège justement : il ne faut absolument pas s'appuyer sur le temps si ce n'est pas explicitement spécifié dans le protocole car, d'une part, si le serveur rame un peu, le client va interpréter cela à tort comme une fin de transmission et, d'autre part, cela imposerait la définition d'une durée minimale fixe qui plomberait lourdement les taux de transferts relatifs à ce protocole lorsque les réseaux évolueraient et gagneraient en rapidité. 10 ms sur un 10baseT, c'est déjà beaucoup, la même chose sur un Gigabit Ethernet et ça deviendrait catastrophique.

    Citation Envoyé par n5Rzn1D9dC Voir le message
    Il y a juste un truc qui pose problème dans mon code de gestion des segments tcp. La "windows size". J'avais mal compris sont intérêt.
    Ca permet de connaître le nombre de byte avant de devoir envoyer un ack.
    Moi je calculais et envoyait le ack après chaque push+ack.
    Je n'avais pas remarqué le problème jusqu'à présent, mais là en faite ce qu'il se passe c'est que mon code envoit le "mail from" avant le ack du 2ème "250" du "ehlo".
    Ce qui fait que le serveur me renvoit ce 2ème "250", pour une raison obscure pour le moment, car le ack est parfaitement calculé.
    Le TCP est fait pour gérer une connexion de type flux bidirectionnel et ne doit donc pas du tout être utilisé pour envoyer des datagrammes distincts. En tout cas, c'est une notion qui ne doit pas du tout apparaître au niveau applicatif.

    Et heureusement parce que tes trames TCP peuvent être découpées en fragments plus petits au cours de leur voyage sur le réseau.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Un pipe est un descripteur de fichier en écriture qui débouche immédiatement sur un autre descripteur en lecture, sans passer par le disque. Ce tube est doté d'un tampon minimum dont tu peux obtenir la taille avec ulimit -p (en blocs de 512 octets). Généralement, la taille de ce tube est fixé à 4096 octets. Un segment TCP tourne souvent entre 1500 octets typiques et 65000 pour les jumbos. Donc, difficile de trancher
    Ce sera uniquement pour du tcpip v4, et peut être v6 quand ce sera obligatoire. Donc niveau taille ça ira finalement.
    C'est surtout pour m'amuser pour le moment, j'ai toujours été passionné par le réseau.
    Au delà de ça, je ne sais même pas si il y aurait moyen de contribuer pour Linux avec un code source asm, ou si ça doit forcément être écrit en C.


    C'est le principe de toute file d'attente et ça a été l'objet de nombreuses études, notamment par Dijkstra. Il y a les histoires de deadlock, les « famines processus », etc. Mais l'idée générale reste que cela ne sert à rien de lancer deux processus en parallèle si le premier doit attendre que le second ait fini pour continuer son propre travail.
    Ouep, j'ai lu le wiki de ce mec et les liens proposés. C'était assez interessant.
    Sinon pour mon code je suis repartie sur une version mono processus. Et ça fonctionne pareil.
    D'ailleurs je ne sais pas pourquoi j'en avais conclus que c'était impossible. Faut dire que ça embrouille pas mal ces histoires de synchro.

    Et voilà ! La RFC spécifie que lorsqu'on réponse tient sur plusieurs lignes, le code est immédiatement suivi d'un « - », qu'on retrouve donc sur toutes les lignes sauf la dernière.

    C'est un peu l'équivalent du « \ » en fin de ligne du langage C et assimilé. Cette réponse est assimilée à une seule et unique grande ligne et le code qui redémarre après chaque retour est censé être le même.
    J'ignorais cela. Et si j'ai eu ce problème c'est parce qu'à l'époque j'avais pris exemple sur un code smtp C (c'était quand je débutais et que j'ai fait un mois de C) pour écrire mon client smtp.
    J'avais vu que le mec s'était basé uniquement sur les codes smtp sans la suite, donc j'en avais conclus que c'était suffisant, bah non finalement...
    Pourtant le code n'est pas écrit par un débutant. Je l'ai relus hier justement pour revoir comment il avait gérer ça, et c'est plutôt bien codé, même si je connais peu le C, ça ce voit direct que le mec code depuis un moment.
    Donc pour le coup, il a fait une erreur. Son code devait fonctionner parfaitement, de même que le miens, mais bon, là vu mon code est trop rapide bah ça bloque direct.

    Désormais je me baserais uniquement sur les rfc.
    En tout cas ça me rassure car je trouverais ça trop bizarre ce double message 250 sans possibilité de le distinguer.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    C'est bon maintenant ça fonctionne niquel avec un simple:
    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
     
         @@:
    	call	data_recv
     
    	call	write
     
    	mov	esi,rbuf
    	mov	cx,word [rbuflen]
         A1:
    	inc	esi
    	dec	cx
    	jz	@b
    	cmp	byte [esi],'2'
    	jnz	A1
    	cmp	byte [esi+1],'5'
    	jnz	A1
    	cmp	byte [esi+2],'0'
    	jnz	A1
    	cmp	byte [esi+3],' '
    	jnz	A1
    Le "250 " (donc le 3ème) du ehlo n'est pas en début de chaine, mais dans le 2ème push+ack, qui contient deux "250":
    - Un "250-"
    - Un "250 "

    Ce qui m'oblige à scanner la chaine pour pouvoir passer le ehlo.

    Par contre, j'ai l'impression que pour que winsize soit pris en compte, il faut le notifier dans les options tcp.
    Bref, vu que le problème ne venait pas de ça au final, j'ai remis le systême de ack après après chaque push+ack, ça fonctionne parfaitement maintenant. En dehors d'un blocage mystérieux de mon programme après l'envoi du "mail from". Mais ça ne doit pas être grand chose.
    En tout cas là ça réagit correctement. Tout est bien envoyé et reçu pile au bon moment sans décalage.
    Ce qui rend la chose pas simple, c'est que ce n'est pas vraiment analysable au debugger, car il ne va pas assez vite, quelque soit le mode.

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Mars 2013
    Messages
    397
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2013
    Messages : 397
    Points : 424
    Points
    424
    Par défaut
    Pour le blocage, c'est juste que le serveur n'envoyait pas le ack du "mail from" avant d'envoyer son "250" comme pour les autres, du coup ça bloquait le code.
    J'ai enlevé le contrôle des ack pour le moment, et ça fonctionne niquel.
    J'ai peux envoyer toutes les commandes jusqu'au "data". Tout est synchro et sans aucune erreur du style retransmission ou autre.

    Il faut que j'étudie de nouveau le systême de ack avec la winsize dans la rfc, afin de faire quelque chose de fiable.
    Concernant l'histoire de double ou mono-processus, je verrais par la suite, mais pour l'instant ça fonctionne.
    En tout cas merci pour les infos.
    Je met le topique comme résolu, il n'y a rien de plus à dire je pense.

    a+

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 07/04/2006, 13h35
  2. Pipes - Comment faire ?
    Par Neitsa dans le forum x86 32-bits / 64-bits
    Réponses: 4
    Dernier message: 11/12/2003, 05h44
  3. [C/S] Boken Pipe
    Par Gogoye dans le forum POSIX
    Réponses: 4
    Dernier message: 23/10/2003, 10h48
  4. Réponses: 3
    Dernier message: 21/08/2003, 14h47
  5. Problème : bloquage d'un pipe
    Par Nicaisse dans le forum POSIX
    Réponses: 10
    Dernier message: 24/07/2003, 11h06

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