Forum Programmation.c Les Sockets, et la taille en reception

Posté par  .
Étiquettes :
0
3
déc.
2007
Bonjour à tous,

Voici des années que je planche sur un gros problème avec les sockets en C, et je me décide enfin a poser la question parce que je ne trouve jamais rien de satisfaisant.

Comment récupérer de façon fiable une trame réseau ? Mon problème est que je n'ai jamais réussi à être certain de la quantité de donné à lire pour obtenir complètement UNE trame. Soit j'en lis trop, soit pas assez.

Je pensais avoir résolu le problème avec un ioctl(m_socket, FIONREAD, &toread), qui me donner la bonne taille dans la plupart des cas, mais je viens de m'apercevoir que si la trame est grosse (> 40000 octets) j'ai fréquement une taille plus petite.
Je me doute que c'est normal, car ça me renvoie la taille de ce qui est contenu dans le buffer, et que dans certain cas, tout n'est pas encore arrivé quand j'utilise cette fonction. Mais je ne peux pas lire avec un while, car quand les trames s'enchainent, des fois, j'ai le début de la trame suivante à la fin de ma trame.

Mais dans ce cas, COMMENT savoir quand je dois arrêter de lire ma socket ? Y'a t-il une fonction miracle qui m'envoie un équivalent d'un EOF quand je suis au bout de ma trame, où autre chose dans ce genre ?

Pour tout dire, dans un protocole fait maison, j'avais même ajouter dans les 4 premiers octets le nombre d'octets qui suivait pour savoir quoi lire. Mais ici, je fait un mini serveur HTTP, et donc, je ne peux évidement pas toucher aux trames issues des navigateurs. Donc, comment faire quand j'ai un grosse requête (oui, oui, j'arrive a en faire des grosses, avec les textarea).

Je vous remercie beaucoup si vous pouviez me soulager de ce problème que je n'ai jamais réussi a résoudre.

Snarky
  • # Ben...

    Posté par  . Évalué à 3.

    Comment récupérer de façon fiable une trame réseau ? Mon problème est que je n'ai jamais réussi à être certain de la quantité de donné à lire pour obtenir complètement UNE trame. Soit j'en lis trop, soit pas assez.

    Ca depend du protocole.

    Avec SNMP par exemple, t'as la taille dans l'entete du header ASN.1

    Avec HTTP, faut lire jusqu'a ce que tu trouves 2 retours de chariot, ce qui te donnera le header complet, et t'as la taille du corps de la reponse dans l'attribut Content-Length du header, si c'est du chunked c'est plus complique mais pas bcp.
    • [^] # Re: Ben...

      Posté par  . Évalué à 2.

      Donc il n'y pas de méthode pour récuperer dans soucis la taille sans connaitre le protocole ?? Pourtant je pensais que le protocole IP contenait cette taille... Ca ne remonterai jusqu'a la couche application ??!
      • [^] # Re: Ben...

        Posté par  . Évalué à 5.

        Je te conseillerai d'aller etudier les protocoles TCP/IP un peu, il semble que tu aies certains coins sombres de ce cote la.

        Les protocoles sont couche sur couche, ca ne veut pas dire qu'un packet IP connaisse, ou meme se soucie de la taille d'une requete HTTP.

        HTTP est (d'habitude) au dessus de TCP, qui lui est d'habitude au dessus de IP.

        HTTP connait la taille de la requete.
        TCP s'en fout, c'est ce qu'on appelle un "streamed" protocole: tu ecris des donnees dans le tube, il envoie ce que tu ecris, sans savoir a l'avance combien tu vas ecrire au total. Il attend genre 200ms que tu ecrives, et il envoie tout ce que tu as ecris jusque la.

        IP en dessous est un protocol qui n'a aucun concept de connection, il envoie des bouts de donnees l'un apres l'autre, il n'a lui-meme aucune connaissance de la taille totale des donees envoyees et ne sait meme pas si 2 paquets qu'il envoie font partie de la meme requete TCP ou HTTP.
        • [^] # Re: Ben...

          Posté par  . Évalué à 2.

          Oui, mais pour moi, le découpage n'etait pas au niveau HTTP, mais au niveau IP (car c'est lui qui interdit des trames dépassant une certaine longueur, et qui les découpes en plusieurs).

          Enfin bon, je vais retourner revoir ce que contient les headers des couches pour être sûr :-p
          • [^] # Re: Ben...

            Posté par  . Évalué à 3.

            De deux choses l'une :

            1) tu utilises un protocole type connexion (comme TCP) et dans ce cas, tu considères que ta socket est à traiter comme l'entrée standard, c'est-à-dire un flux continu de données. Tu sais donc que la transmission est terminée lorsque ta connexion se referme. Accessoirement, c'est comme ça que fonctionne HTTP (par défaut stateless et aux connexions non persistantes).

            2) tu utilises un protocole à paquets (comme UDP) et dans ce cas, chaque datagramme est en lui-même indépendant des autres, mais tu le reçoit en entier. Tu peux utiliser ce mode si tu veux envoyer des "unités" de données sans te faire suer à savoir si c'est terminé ou pas. Cela a ses avantages et ses inconvénients.

            Travailler directement au niveau de l'IP n'est pas une bonne idée si tu travaille sur une application de haut-niveau, spécialement si tu réimplémente un protocole qui a été conçu pour circuler au travers d'une liaison streaming sûre.
            • [^] # UDP versus TCP (pour des ex-protocoles sur liaison série)

              Posté par  (site web personnel) . Évalué à 4.

              Dans le domaine industriel, souvent les gens de base s'orientent vers TCP pour des protocoles de types question/réponses qui tiennent sur une simple trame ethernet au niveau longueur (et à l'origine qui fonctionnaient sur des liaisons séries), et je dois souvent me justifier pour expliquer et convaincre pourquoi dans ce cas UDP se défend vraiment...
              il y a peut-être aussi le fait que beaucoup de serveurs de port/convertisseur IP<->série fonctionne dans ce mode en général, bien que certains modèles commencent enfin gérer l'UDP.
              (et qu'à l'école rapidement on vous sort qu'UDP est pas sécurisé et vite on passe à TCP...)

              Pour des petites trames de type questions/réponses avec un maître qui sait s'il a reçu la réponse ou pas et qui repose si besoin est sa question (si réponse non reçue), je trouve que c'est bien adapté et moins lourd que TCP (et en série, le maître devait déjà gérer les réémissions nécessaires...)
              ça a aussi l'énorme avantage en embarqué de pouvoir permettre la gestion de multiples clients simultanément sans traitement particulier au niveau du serveur: on répond à la question de celui qui l'a posé, et on passe au suivant: le même ou un autre serveur...
              et y'a aussi le brodcast possible dans ce mode!

              enfin voilà, tout ça pour dire qu'UDP c'est quand même pas mal et qu'on a souvent trop vite fait tendance à le mettre de côté.
              • [^] # Re: UDP versus TCP (pour des ex-protocoles sur liaison série)

                Posté par  (site web personnel) . Évalué à 2.

                et je dois souvent me justifier pour expliquer et convaincre pourquoi dans ce cas UDP se défend vraiment

                Pour casse ton adoration envers UDP, juste une chose : avec UDP, tu n'as aucune garantie que la trame est érrivée à destination, c'et c'est chiant.
                Avec TCP, même avec une trame, tu sais si la trame est arrivée ou pas à destination, c'est un énorme plus.
                Et par rapport à ton jeu question/réponse, tu réponds comment à l'emeteur si tu es en UDP, surtout en NAT? L'emeteur a fermé sa connexion... (ou alors, j'ai pas compris UDP!)
                • [^] # Re: UDP versus TCP (pour des ex-protocoles sur liaison série)

                  Posté par  . Évalué à 1.

                  tu réponds comment à l'emeteur si tu es en UDP, surtout en NAT?
                  Les réponses UDP passent sans problème à travers un firewall qui fait du NAT.
                  C'est le module conntrack qui gère cela en créant une association temporaire entre l'émetteur et le destinataire dans sa table interne de suivi des connexions.
                  Heureusement que ça marche, sinon un certain nombre de services indispensables comme DNS ne pourraient pas fonctionner !
                • [^] # Re: UDP versus TCP (pour des ex-protocoles sur liaison série)

                  Posté par  (site web personnel) . Évalué à 1.

                  Très souvent sur les protocoles industriels, typiquement on récupère l'état de l'équipement... donc si pas reçu (bah on le sait aussi en UDP, on sera passé en timeout!) de toute manières les informations perdues après réémission par la couche TCP ne seraient plus à jour, donc autant en redemander de nouvelles "up to date" !
                  C'est des données temps-réel qu'on récupère, pas une partie de données hyper importantes à assembler ;-)
          • [^] # Re: Ben...

            Posté par  . Évalué à 4.

            Quand tu fais un send() ou un recv() sur un socket, cela n'a absolument RIEN a voir avec la taille d'un paquet IP(sauf si tu joues avec les raw sockets).

            Si tu fais un send de 300 bytes, il pourrait envoyer 1, voire 2, voire encore plus de paquets IP, de meme, quand tu fais un recv() il pourrait te renvoyer le contenu d'un, deux, trois ou meme un demi ou un tiers de paquet IP.
          • [^] # Re: Ben...

            Posté par  . Évalué à 1.

            Bon, il faut faire comme on t'as dit. si tes socket son bloquantes en lecture, tu vas devoir lire les données octet par octet et attendre qu'un double retour chariot se présente. une fois détecté, ton serveur pourra interpréter la requête, la traiter, renvoyer la réponse et couper la connection avec le client.
            Mais, bon, pour un serveur web, ou tout autre type de serveur d'ailleurs, je te conseille de faire la gestion des entrées sorties en mode non bloquant. premier avantage, tu va pouvoir lire jusqu'à ce que la socket te renvoit un message EAGAIN, et ce que tu as lu, tu le stocke dans un buffer.
            Ensuite, il ne te reste plus qu'à lire ton buffer, et trouver un double retour chariot. s'il n'y en a pas, tu devra attendre que d'autres données arrivent... et ainsi de suite...c'est plus performant.
            Pour savoir s'il y a quelque chose à lire dans une socket, y a select() (le seul qui soit presque portable), sinon sous linux, je te conseille vivement epoll(), qui fait des merveilles...

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.