Developpez.com - Rubrique Pascal

Le Club des Développeurs et IT Pro

Accélérer le téléchargement de fichiers en Delphi

à l'aide de plusieurs connexions et d'un fichier mappé, par Andnotor

Le 2012-12-11 12:12:10, par Andnotor, Rédacteur/Modérateur
Bonjour à tous !

Je vous propose aujourd'hui un tutoriel sur l'accélération du téléchargement de fichiers.

Vous disposez d'un accès Internet haut débit mais trouvez que le téléchargement est long et que la connexion n'est pas utilisée à son maximum ?
Vous pensez à un problème de configuration du router ? A un ordinateur trop ancien ? Pas du tout ! La raison est que l'hébergeur du site limite la bande passante.

Ce tutoriel va vous expliquer comment passer outre cette limitation à l'aide de plusieurs connexions et d'un fichier mappé.

N'hésitez pas à faire part de vos réactions et commentaires
  Discussion forum
23 commentaires
  • Andnotor
    Rédacteur/Modérateur
    Envoyé par Paul TOTH
    ça n'a rien à voir, c'est le fonctionnement même de HTTP, tu télécharges un document HTML qui possède des liens vers d'autres ressources qui sont téléchargées (ou pas selon le cache) à leur tour.
    La page html oui, ensuite plusieurs connexions sont ouvertes pour télécharger son contenu (le cache local c'est une autres histoire). IE8 utilise par défaut jusqu'à 6 connexions.

    Envoyé par Paul TOTH
    un bon moyen de rendre dingue le serveur
    Mais est-ce le même serveur (Load-balancing) ?

    Envoyé par Paul TOTH
    ...mais il ne faut pas demander aux serveurs de fournir 15M à chaque client !
    Mais pourquoi pas Pourquoi partez-vous du principe que le serveur est toujours utilisé à 100% ?
    C'est comme si j'avais un CPU six coeurs mais que le multitâche était interdit au cas où d'autres "clients" en avaient besoin !

    Que la bande passante diminue en fonction du nombre d'accès concurrents, je peux le concevoir et l'admettre. Qu'elle soit limitée au cas où est plus difficile à avaler !

    Maintenant, mon but n'était pas d'ouvrir un débat sur le mode d'accès Internet, sur la politesse envers les autres utilisateurs, sur les cheveux blancs que vont immanquablement se faire les webmasters et encore moins m'attirer leur foudre ! L'idée n'était pas non plus de saturer la connexion d'une PME ou de mettre leur serveur (local) à plat parce qu'un gugusse a décider de télécharger une grosse mise à jour plein pot pendant les heures de bureau !
    Mais oui, lorsque j'ai commencé mes mesures, j'avais envie de voir jusqu'où on pouvait aller...
  • ShaiLeTroll
    Expert éminent sénior
    Pas mal du tout !
    Pour les curieux, un lien vers HTTP : le protocole du Web passé en revue - Transactions partielles expliquant le Range qui me semble un pré-requis nécessaire pour comprendre la partie "HTTP Server" exploité par ton tutoriel

    Au lieu d'un format sur Range (qui est commenté dans le source comme deprecated), autant passer directement par Ranges

    Code :
    1
    2
    3
          //Télécharge un bloc
          Http.Request.Range := Format('%d-%d',[View.Offset, View.Offset +View.Size -1]);
    Deviendrait
    Code :
    1
    2
    3
    4
    5
    6
        with Http.Request.Ranges.Add() do
        begin
          StartPos := View.Offset;
          EndPos := View.Offset +View.Size -1;
        end;

    Cela renvoit-il une exception en cas de refus du Get du HTTP Range-Request - Error 416 ?
    Ne faudrait-il pas protéger les threads de cela ?
  • Rayek
    Modérateur
    Envoyé par Montor
    Intéressant

    Ceci ne devrais pas fonctionner sous D7 version Indy obsolète
    a noter que certains site empêchent d'ouvrir plusieurs entêtes de téléchargements en même temps
    Tu peux mettre à jour tes composants Indy avec les nouvelles versions
  • Andnotor
    Rédacteur/Modérateur
    Et de quels hébergeurs parles-tu ? Des mêmes gentlemen qui te vendent une bande passante et des connexions illimitées 24/7 ?

    Envoyé par Freem
    ...qui m'avais expliqué pourquoi les outils de téléchargement qui ouvrent plusieurs sockets font chier.
    Et bien explique-nous ! A part multiplier les clients et les bénefs... je vois pas
  • Andnotor
    Rédacteur/Modérateur
    Envoyé par RaphAstronome
    Effectivement ce genre de comportement sur-consomme les ressources serveur et ralenti voir à l'extrême rend inaccessible le serveur pour les autres clients.
    Et même là, je ne vois pas trop le problème !
    Surconsommation ? Je demande à télécharger un fichier d'une certaine taille, que ça prenne 1 minute ou une heure, j'aurai consommé la même bande passante.
    Les ressources du serveur ? Sans être un expert, je pense qu'elles sont plus mises à rude épreuve en exécutant le même volume de données en scripts php que par du téléchargement

    Le principe est que chaque tâche se connecte et se déconnecte (à moins d'un keep-alive accepté, mais rare) et télécharge moins d'un méga (Ce n'est pas la taille du fichier divisé par N threads). Si le serveur est "chargé" et retarde la Nième demande, ben voilà... on attend ! Mais s'il n'a rien à faire, Pourquoi se tourner les pouces

    Je regrette cependant que vous notiez ce tutoriel d'un étoile uniquement pour l'idée qu'il véhicule et non pour son contenu. Mais... l'avez-vous même lu ?
  • Andnotor
    Rédacteur/Modérateur
    Il y a un test de la version du compilateur qui conditionne la déclaration de constantes et fonctions. Il est réglé sur D7 et antérieurs (< 16) mais d'après ce que tu dis, il faudrait le mettre sur 18.5 minimum.

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    {$IF CompilerVersion < 18.5}
    const
      FILE_READ_DATA  = $0001;
      FILE_WRITE_DATA = $0002;
    
      procedure GetNativeSystemInfo(var aSystemInformation: TSystemInfo); stdcall; external kernel32;
      function  SetFilePointerEx(aFile: THandle; aDistance :int64; const aPointer :PInt64; aMethod :dword): bool; stdcall; external kernel32;
    {$IFEND}
    Erreur sur Create :
    Code :
    inherited Create(FALSE);
    CPUCount n'est pas très important. Tu peux le remplacer par 2.
    Si tu veux cependant te baser sur le nombre de CPUs, tu peux également récupérer cette valeur par GetSystemInfo (dwNumberOfProcessors).
  • Andnotor
    Rédacteur/Modérateur
    Je vais ajouter ce lien en référence

    Envoyé par ShaiLeTroll
    Au lieu d'un format sur Range (qui est commenté dans le source comme deprecated), autant passer directement par Ranges
    J'admets ne pas avoir été voir l'implémentation de Range. Un deprecated en commentaire n'attire pas vraiment l'attention
    Au départ, je voulais même partir sur WinINet (ou WinHTTP) mais je me suis dit que l'intérêt de ce tuto n'était pas tellement la connexion elle-même (à part Range(s)). J'ai fait au plus simple

    Envoyé par ShaiLeTroll
    Cela renvoit-il une exception en cas de refus du Get du HTTP Range-Request - Error 416 ?
    Ne faudrait-il pas protéger les threads de cela ?
    Le test est fait en amont à l'appel de HEAD AcceptRanges := Response.AcceptRanges = 'bytes';. Si le téléchargement par fragment n'est pas supporté, c'est la méthode SingleDownload (GET sur TFileStream directement) qui est invoquée.
  • ShaiLeTroll
    Expert éminent sénior
    Envoyé par Andnotor
    Le test est fait en amont à l'appel ...
    OK, je ne suis pas allé aussi loin dans ton tutoriel, honte à moi de ne pas avoir tout lu
  • Montor
    Membre éprouvé
    Intéressant

    Ceci ne devrais pas fonctionner sous D7 version Indy obsolète
    a noter que certains site empêchent d'ouvrir plusieurs entêtes de téléchargements en même temps
  • BuzzLeclaire
    Membre éprouvé
    Dommage que ma connexion et surtout mon poste, n'a pas pu apporter d'eau à ton moulin.

    En tous cas c'est trés pratique tout de même.

    gg.