I. Introduction▲
Qui n'a jamais trouvé le téléchargement d'un fichier de plusieurs dizaines de mégas extrêmement long malgré une connexion Internet haut débit ? Vous ? Moi ?… Tout le monde !
Ce tutoriel va vous expliquer comment en accélérer le téléchargement à l'aide de plusieurs connexions simultanées en multi-threads et d'un fichier mappé.
Remarque :
Il ne s'agit pas de transformer un accès Internet à 15 Mb/s en un à 25 (faut pas rêver) mais simplement de passer outre la limitation de bande passante définie par l'hébergeur du site visé. Il faut par conséquent que le débit descendant de votre connexion soit supérieur à la limite en place. Dans le cas contraire, vous ne remarquerez aucune différence !
II. Introduction aux fichiers mappés▲
Ce tutoriel n'a pas pour but d'expliquer tous les détails du mappage de fichier. En voici seulement une courte introduction.
Un fichier mappé est l'association entre le contenu d'un fichier et une zone de la mémoire virtuelle d'un ou de plusieurs processus. C'est un pont entre le fichier et les processus.
Une vue est la portion de la zone de mémoire virtuelle qu'un processus utilise pour accéder au contenu du fichier. Elle peut représenter le fichier complet ou une partie seulement. En d'autres termes, une vue est un pointeur sur un fragment du fichier chargé dans la mémoire du processus.
III. Principe▲
Sans grande surprise, le but va être de lancer plusieurs connexions Internet simultanément et à travers chacune d'elles de télécharger un fragment du fichier.
Chaque connexion est réalisée dans un thread séparé (TViewDownloadThread). Ils sont créés au démarrage et synchronisés par Events.
Un thread principal (TDownloadThread) se charge de répartir le travail en fonction de l'occupation des threads de téléchargement.
Le téléchargement est réalisé à l'aide du composant INDY : TIdHttp.
IV. Nombre de tâches▲
Le nombre de tâches peut être supérieur au nombre de processeurs de la machine. On peut en effet partir du principe que le dialogue client/serveur à travers Internet est lent et que chaque thread passera un certain temps à attendre des réponses.
Il y a cependant certaines limitations qu'il serait inutile de dépasser :
- une vue ne peut pas commencer à n'importe quel endroit d'un fichier mappé, elle doit respecter un alignement déterminé par le système. Cet alignement est récupéré à l'aide de l'API GetNativeSystemInfo. Il correspond à la « granularité d'allocation » et est normalement de 64 kB sous Windows.
Dès lors, le nombre de threads ne devrait excéder : la taille du fichier à télécharger div 64 kB + 1. Si le fichier faisait 200 kB, le nombre de threads utilisés serait de quatre, même si on en créait dix.
Dans notre exemple, la taille d'un bloc sera fixée à 15 fois l'allocation minimale. Un thread téléchargera donc environ 975 kB ce qui est un bon compromis entre l'alignement obligatoire des vues et le temps de latence. Il faudra cependant réduire ce multiplicateur si le débit de la connexion Internet est inférieur à 15 Mb/s.
Voici comment récupérer la taille d'allocation minimale :
var
SystemInfo :TSystemInfo;
AllocSize :Int64;
begin
GetNativeSystemInfo(SystemInfo);
AllocSize := SystemInfo.dwAllocationGranularity;
end
;
- la vitesse de la connexion Internet (downstream). 10 Mb/s permettraient dans le meilleur des cas une quinzaine de threads si 64 kB était utilisé ;
- la qualité du service fourni par l'hébergeur du site. Limite de bande passante et du nombre de connexions concurrentes, temps de latence, etc. Certains serveurs n'acceptent tout simplement pas le téléchargement par fragment !
Créer plus de tâches que nécessaire a cependant un impact négligeable sur la fonction proposée ci-dessous. Les inutilisées sont placées en mode attente infinie par WaitForSingleObject(INFINITE) et ne consomment par conséquent aucun temps processeur.
V. Tâche de téléchargement▲
Les tâches de téléchargement sont en charge de récupérer un fragment du fichier. Elles pourraient être créées uniquement lorsqu'un nouveau bloc est requis. Ici, elles sont créées une fois pour toutes au démarrage et synchronisées par Events.
Type
TViewDownloadThread = class
(TThread)
private
URL :string
;
MapFile :THandle;
View :TViewInfo;
Stream :TMemoryStream;
Http :TIdHttp;
RunEvent :THandle;
ReadyEvent :Thandle;
end
;
- URL est le fichier à télécharger.
- MapFile est le fichier mappé auquel nous appliquerons une vue.
- View est une structure contenant l'offset et la taille du bloc à télécharger.
- Stream est le buffer de téléchargement.
- Http est un TidHttp en charge de la connexion au serveur.
- RunEvent est un Event local ordonnant le démarrage de la fonction en attente.
- ReadyEvent est un Event fourni par le thread principal et signifie la fin du téléchargement du bloc et l'état prêt pour une nouvelle commande.
TViewInfo est déclaré ainsi :
TViewInfo = record
Size :int64;
case
byte
of
0
: (Offset :int64);
1
: (LoOffset, HiOffset :integer
);
end
;
Size est la taille du bloc à télécharger. Le case permet de lire/écrire l'offset sous forme d'un int64 ou de deux entiers LowInteger et HighInteger sans devoir appliquer de transformation. MapViewOfFile que nous utiliserons bientôt requiert la deuxième forme.
V-A. Création et destruction▲
Les paramètres de création sont mémorisés et l'Event de démarrage ainsi que les objets nécessaires au téléchargement sont créés.
constructor
TViewDownloadThread.Create(aURL :string
; aMapFile, aReadyEvent :THandle; aOnTerminate :TNotifyEvent);
begin
inherited
Create;
URL := aURL;
MapFile := aMapFile;
ReadyEvent := aReadyEvent;
OnTerminate := aOnTerminate;
RunEvent := CreateEvent(nil
, TRUE
, FALSE
, nil
);
Stream := TMemoryStream.Create;
Http := TIdHTTP.Create(nil
);
HTTP.Request.Connection := 'keep-alive'
;
Http.Request.UserAgent := UserAgent;
//Libération automatique
FreeOnTerminate := TRUE
;
end
;
OnTerminate est défini sur une méthode de la tâche principale et permet de tester la fin du déroulement du thread par l'intermédiaire de la propriété ReturnValue.
Qui dit création, dit destruction. Voici le destructeur.
destructor
TViewDownloadThread.Destroy;
begin
CloseHandle(RunEvent);
Stream.Free;
Http.Free;
inherited
;
end
;
V-B. Start▲
Cette méthode est appelée depuis la tâche principale. Les offset et taille sont fixés, le thread est marqué comme occupé et enfin démarré.
procedure
TViewDownloadThread.Start(aOffset, aSize: int64);
begin
View.Offset := aOffset;
View.Size := aSize;
//Occupé et démarré
ResetEvent(ReadyEvent);
SetEvent(RunEvent);
end
;
V-C. Stop▲
Le principe des threads de téléchargement est de ne pas générer d'activité s'il n'y a pas de bloc à récupérer. Ils sont mis dans un état d'attente infini. Pour les détruire, il est donc nécessaire de les réveiller pour leur permettre de sortir de la boucle de répétition.
Stop est appelé depuis la tâche principale une fois le téléchargement terminé.
procedure
TViewDownloadThread.Stop;
begin
Terminate;
SetEvent(RunEvent);
end
;
V-D. Execute▲
Voici comment se déroule le téléchargement d'un bloc une fois le démarrage signalé (RunEvent).
La sélection d'un fragment du fichier distant se fait en spécifiant dans l'entête de la requête les pointeurs de début et fin de bloc. Request.Range de TIdHttp est une chaîne de la forme début-fin.
Get récupère le bloc et remplit un stream. Si l'opération s'est bien déroulée, la vue sur le fichier mappé est créée en écriture et les données sont copiées en bit à bit du stream vers la vue.
procedure
TViewDownloadThread.Execute;
var
Data :pointer
;
begin
while
not
Terminated do
begin
//Attente infinie de l'ordre de démarrage
WaitForSingleObject(RunEvent, INFINITE);
if
not
Terminated then
try
ReturnValue := ERROR_FILE_NOT_FOUND;
Stream.Clear;
//Télécharge un bloc
Http.Request.Range := Format('%d-%d'
,[View.Offset, View.Offset +View.Size -1
]);
Http.Get(URL, Stream);
//Ouvre en écriture la vue correspondante du fichier local
Data := MapViewOfFile(MapFile, FILE_MAP_WRITE, View.HiOffset, View.LoOffset, View.Size);
if
not
Assigned(Data) then
begin
ReturnValue := GetLastError;
Exit;
end
;
//Copie les données et libère la vue
CopyMemory(Data, Stream.Memory, View.Size);
UnmapViewOfFile(Data);
ReturnValue := ERROR_SUCCESS;
finally
//Arrêté et prêt
ResetEvent(RunEvent);
SetEvent(ReadyEvent);
end
;
end
;
end
;
La vue est ensuite libérée et la tâche à nouveau marquée comme arrêtée et prête pour un nouvel ordre.
VI. Tâche principale▲
Le thread principal se charge de l'accès au fichier local, de la gestion des threads de téléchargement et de la notification d'avancement.
Voici la déclaration de ses propriétés :
type
TDownloadThread = class
(TThread)
private
Wnd :hWnd;
Message
:Cardinal
;
URL :string
;
FileName :TFileName;
FileSize :int64;
AllocSize :int64;
ThreadCount :integer
;
Error :boolean
;
end
;
- Wnd et Message vont nous permettre d'envoyer des informations sur l'état du téléchargement par PostMessage. Message peut correspondre à une constante WM_USER ou à un message enregistré par RegisterWindowMessage.
- URL est bien entendu le fichier à télécharger et FileName le fichier local. L'URL doit commencer par « http:// ».
- FileSize est la taille totale du fichier.
- AllocSize est la taille d'un fragment de ce fichier.
- ThreadCount est le nombre de threads à utiliser.
- Error est un booléen passant à VRAI si une erreur est rencontrée dans la tâche principale ou dans celles de téléchargement.
En plus de ces propriétés, la variable TThread.ReturnValue est utilisée et contient l'état du téléchargement. ERROR_SUCCESS en cas de réussite, le code d'erreur d'une API ayant échoué ou ERROR_FILE_NOT_FOUND si le fichier distant n'a pu être localisé. ReturnValue est utilisé pour notifier l'erreur à l'utilisateur.
VI-A. Création▲
Rien de bien compliqué !
Les paramètres sont mémorisés, le nombre de tâches de téléchargement est fixé au nombre de CPU x 2 si 0 est passé à la fonction. La taille d'un bloc est définie en fonction de l'allocation minimale récupérée et le TThread est marqué comme autodétruit.
constructor
TDownloadThread.Create(aWnd :hWnd; aMessage :Cardinal
; aURL :string
; aFileName :TFileName; aThreadCount :integer
);
var
SystemInfo :TSystemInfo;
begin
inherited
Create;
Wnd := aWnd;
URL := aURL;
Message
:= aMessage;
FileName := aFileName;
Error := FALSE
;
//Fixe le nombre de threads à utiliser
if
aThreadCount > 0
then
ThreadCount := aThreadCount
else
ThreadCount := CPUCount *2
;
//Fixe la taille d'allocation minimale définie par le système
GetNativeSystemInfo(SystemInfo);
AllocSize := SystemInfo.dwAllocationGranularity *15
;
//Libération automatique
FreeOnTerminate := TRUE
;
end
;
VI-B. Notification▲
Notification asynchrone par PostMessage de l'avancement du téléchargement. WParam est le type de notification. LParam dépend de ce type.
type
TFastURLDownloadState = (dsStart, dsIncrement, dsError, dsSuccess);
procedure
TDownloadThread.Notify(aState: TFastURLDownloadState; aLParam: LParam);
begin
PostMessage(Wnd, Message
, WParam(aState), aLParam);
end
;
- dsStart est envoyé en début du téléchargement mais après que la taille du fichier a été récupérée depuis le serveur. LParam contient la taille du fichier.
- dsIncrement est envoyé au démarrage de chaque thread de téléchargement. LParam contient la position actuelle.
- dsError est envoyé en cas d'erreur. LParam est le code d'erreur.
- dsSuccess est envoyé lorsque le téléchargement s'est terminé avec succès. LParam contient la durée en millisecondes.
VI-C. OnTerminate▲
OnTerminate est l'événement généré à la fin du téléchargement d'un bloc par un thread de téléchargement et renseigne sur le succès de l'opération.
procedure
TDownloadThread.OnThreadTerminate(Sender: TObject);
begin
with
TThread(Sender) do
if
ReturnValue <> ERROR_SUCCESS then
begin
Error := TRUE
;
Notify(dsError, ReturnValue);
end
;
end
;
VI-D. Informations sur le fichier et serveur distants▲
Pour connaître la taille du fichier distant et la capacité du serveur à nous en envoyer un fragment, il suffit de récupérer l'entête d'une réponse à l'aide de la méthode Head de TIdHttp et de consulter la variable Response :
- ContentLength : contient la taille du fichier ;
- AcceptRanges : contient « bytes » si le téléchargement par bloc est possible et « none » si non supporté.
Si le fichier n'est pas trouvé, l'erreur ERROR_FILE_NOT_FOUND est générée.
procedure
TDownloadThread.GetFileInfo;
begin
//Récupère la taille du fichier et la capacité du serveur
//en ne téléchargeant que l'entête
try
with
TIdHTTP.Create(nil
) do
try
Request.UserAgent := UserAgent;
Head(Self
.URL);
FileSize := Response.ContentLength;
AcceptRanges := Response.AcceptRanges = 'bytes'
;
finally
Free;
end
;
except
Notify(dsError, ERROR_FILE_NOT_FOUND);
Raise
;
end
;
end
;
VI-E. Téléchargement simple▲
Si la taille du fichier est inférieure à la taille d'allocation d'une vue, si un seul thread est demandé ou tout simplement si le serveur ne supporte pas le téléchargement par bloc, inutile de nous fatiguer ! Un téléchargement simple est lancé à l'aide d'un TFileStream.
procedure
TDownloadThread.SingleDownload;
var
Stream :TFileStream;
begin
//Création du fichier local. "Accès refusé" si impossible
try
Stream := TFileStream.Create(FileName, fmCreate);
except
Notify(dsError, ERROR_ACCESS_DENIED);
Raise
;
end
;
//Téléchargement
try
with
TIdHTTP.Create(nil
) do
try
Request.UserAgent := UserAgent;
Get(Self
.URL, Stream);
finally
Stream.Free;
Free;
end
;
except
Notify(dsError, ERROR_FILE_NOT_FOUND);
Raise
;
end
;
end
;
VI-F. Téléchargement multiple▲
Nous voici au cœur du système, la gestion du téléchargement multiconnexions.
Pour en expliquer clairement le contenu, cette procédure est fragmentée en plusieurs parties et pour plus de lisibilité le contrôle d'erreur a été supprimé. La procédure complète est visible en fin de chapitre.
Déclarations
procedure
TDownloadThread.MultiDownload;
var
FHandle :hFile;
hMap :THandle;
Offset :int64;
Size :int64;
Position :int64;
WaitState :dword;
i :integer
;
ThreadList :array
of
TViewDownloadThread;
ReadyList :array
of
THandle;
- FHandle est le handle du fichier local et hMap du fichier mappé.
- Offset est le décalage de la vue par rapport au début du fichier mappé et Size sa taille.
- Position est une simulation de l'avancement du téléchargement et correspond au nombre de blocs en téléchargement x la taille de leur vue.
- WaitState est utilisé par la synchronisation et détermine qu'un thread est prêt à exécuter un nouveau travail.
- ThreadList est la liste des tâches créées.
- ReadyList est une liste d'Events indiquant l'état prêt de chaque tâche.
Création des fichiers
Le fichier local est créé par CreateFile en lecture et écriture et sa taille définitive est directement allouée. Le fichier mappé est ensuite créé sur le handle retourné.
FHandle := CreateFile(PChar(FileName), FILE_READ_DATA or
FILE_WRITE_DATA, 0
, nil
, CREATE_ALWAYS, 0
, 0
);
SetFilePointerEx(FHandle, FileSize, nil
, FILE_BEGIN);
SetEndOfFile(FHandle);
hMap := CreateFileMapping(FHandle, nil
, PAGE_READWRITE, 0
, 0
, nil
);
Il n'est en effet pas nécessaire d'écrire dans un fichier pour en augmenter sa taille. Assigner son pointeur interne (SetFilePointerEx) au-delà de la fin du fichier est parfaitement valide. Il est ensuite possible de fixer la fin de fichier sur ce pointeur (SetEndOfFile).
Création des Threads et Events
Les tâches de téléchargement sont créées ainsi qu'un nombre identique d'Events. Cette liste d'Events (ReadyList) permet de déterminer lorsqu'une tâche est terminée et prête à recevoir un nouvel ordre.
SetLength(ReadyList, ThreadCount);
SetLength(ThreadList, ThreadCount);
for
i := 0
to
ThreadCount -1
do
begin
ReadyList[i] := CreateEvent(nil
, TRUE
, TRUE
, nil
);
ThreadList[i] := TViewDownloadThread.Create(URL, hMap, ReadyList[i], OnThreadTerminate);
end
;
Boucle de téléchargement
WaitForMultipleObjects est l'objet de synchronisation permettant de savoir qu'au moins un thread de la liste n'a pas de travail en cours. Il est tout de même réveillé toutes les secondes pour s'assurer que la tâche courante n'a pas été terminée (sortie du programme par exemple).
Lorsqu'un thread est prêt, il est redémarré (Start) sur une vue correspondant à Offset et Size.
La boucle est terminée en cas d'erreur ou lorsque Size est plus petit qu'AllocSize signifiant que toutes les données ont été téléchargées ou sont en passe de l'être.
while
not
(Terminated or
Error) do
begin
WaitState := WaitForMultipleObjects(ThreadCount, @ReadyList[0
], FALSE
, 1000
);
if
WaitState <> WAIT_TIMEOUT then
begin
i := WaitState -WAIT_OBJECT_0;
Size := Min(FileSize -Offset, AllocSize);
if
Size > 0
then
begin
ThreadList[i].Start(Offset, Size);
inc(Position, Size);
Notify(dsIncrement, Position);
end
;
if
Size < AllocSize
then
Break
else
inc(Offset, AllocSize);
end
;
end
;
Attente de fin
Spécifier VRAI pour le paramètre bWaitAll ordonne à la fonction WaitForMultipleObjects d'attendre que tous les Events soient signalés. Si nous ne le faisions pas, le thread principal serait détruit avant que le téléchargement soit effectivement terminé.
WaitForMultipleObjects(ThreadCount, @ReadyList[0
], TRUE
, INFINITE);
Les libérations
Le téléchargement est terminé ou une erreur est survenue, il est temps de procéder à quelques nettoyages. Destruction des threads et libération des Events, fermeture des fichiers et effacement du fichier local en cas d'erreur.
for
i := 0
to
ThreadCount -1
do
begin
ThreadList[i].Stop;
CloseHandle(ReadyList[i]);
end
;
CloseHandle(hMap);
CloseHandle(FHandle);
if
Error then
DeleteFile(FileName);
VI-F-1. Procédure complète▲
Voici la procédure complète avec gestion des erreurs et commentaires.
VI-G. Execute▲
Il ne nous reste plus qu'à implémenter la méthode Execute du thread.
C'est ici que nous allons déterminer la taille du fichier à télécharger, le nombre de threads réellement utilisés et par conséquent choisir entre le mode de téléchargement simple ou multiple.
procedure
TDownloadThread.Execute;
var
StartTime :Cardinal
;
begin
//Informations sur le fichier et la capacité du serveur
GetFileInfo;
//Contrôle le nombre de Threads à utiliser
if
ThreadCount > 1
then
begin
if
AcceptRanges and
(FileSize > AllocSize)
then
ThreadCount := Min(ThreadCount, FileSize div
AllocSize)
else
ThreadCount := 1
;
end
;
Notify(dsStart, FileSize);
StartTime := GetTickCount;
//Téléchargement
if
ThreadCount > 1
then
MultiDownload
else
SingleDownload;
//Si pas eu d'erreur avant
Notify(dsSuccess, GetTickCount -StartTime);
end
;
VII. Procédure FastURLDownload▲
FastURLDownload est la procédure accessible depuis l'extérieur de l'unité utilisée pour lancer le téléchargement.
procedure
FastURLDownload(aWnd :hWnd; aMessage :Cardinal
; aURL :string
; aFileName :TFileName; aThreadCount :integer
= 0
);
- aWnd est la fenêtre devant recevoir les notifications d'avancement.
- aMessage est le message à envoyer. Cela peut être une constante WM_USER ou un message enregistré par RegisterWindowMessage.
- aURL est le fichier à télécharger. aURL doit commencer par « http:// »
- aFileName est le fichier local.
- aThreadCount est le nombre de tâches de téléchargement simultanées désirées. Si 0, il correspond au nombre de processeurs disponibles sur la machine x 2.
VIII. Utilisation▲
Et voici comment appeler cette procédure dans votre application.
procedure
TForm1.Button1Click(Sender: TObject);
begin
FastURLDownload(Handle, WM_USER, 'http://www.domaine.com/fichier.exe'
, 'c:\RépertoireLocal\fichier.exe'
, CPUCount *3
);
end
;
Et pour recevoir les notifications, une fiche contenant un mémo et une barre de progression.
type
TForm1 = class
(TForm)
Memo1: TMemo;
ProgressBar1: TprogressBar;
protected
procedure
WMFastDownloadNotify(var
Message
:TMessage); message
WM_USER;
end
;
procedure
TForm1.WMFastDownloadNotify(var
Message
: TMessage);
begin
with
Message
do
case
TFastURLDownloadState(WParam) of
dsStart : //Démarrage
begin
Memo1.Lines.Add('Téléchargement commencé à '
+DateTimeToStr(Now));
Memo1.Lines.Add(Format('Taille du fichier : %d octets'
, [LParam]));
ProgressBar1.Max := LParam;
ProgressBar1.Position := 0
;
end
;
dsIncrement : //Incrémentation
ProgressBar1.Position := LParam;
dsError : //Erreur
Memo1.Lines.Add(Format('Erreur %d : %s'
, [LParam, SysErrorMessage(LParam)]));
dsSuccess : //Terminé avec succès
Memo1.Lines.Add(Format('Téléchargement terminé en %fs'
, [LParam /1000
]));
end
;
end
;
Simple non ?
IX. Mesures▲
Voici quelques mesures réalisées à partir de deux sites différents. Le fichier est identique et correspond à la version Delphi 6 Perso. Sa taille est de 146 646 147 octets.
Le premier site est bien sûr Developpez.net. Le deuxième est hébergé chez Infomaniak.
Ces mesures sont faites sur une modeste machine bicœur tournant à 2 GHz et le débit descendant de la connexion Internet est de 50 Mb/s.
Les tableaux indiquent :
- le nombre de tâches utilisées, soit le nombre de connexions concurrentes ;
- la charge moyenne du système (en pour cent) ;
- le débit Internet maximum (en Mb/s) ;
- le temps total (en secondes).
Une tâche équivaut au téléchargement « simple ». Deux et plus au téléchargement « multiple ».
Accrochez-vous, ça va décoiffer !
Infomaniak :
Nombre de tâches | Charge CPU (%) | Débit Internet (Mb/s) | Temps (s) |
1 | 20 | 26 | 51 |
2 | 45 | 34 | 38 |
4 | 80 | 52 | 24 |
Quatre tâches suffisent pour atteindre le débit maximum de 50 Mb/s.
Voici les graphiques de l'activité des processeurs et du débit :
Deux tâches | Quatre tâches (débit max) |
Developpez.net :
Nombre de tâches | Charge CPU (%) | Débit Internet (Mb/s) | Temps (s) |
1 | 12 | 11 | 172 |
2 | 25 | 20 | 70 |
4 | 60 | 36 | 36 |
10 | 95 | 48 | 32 |
12 | 95 | 52 | 24 |
La bande passante est plus limitée sur le site de DVP. Douze tâches sont nécessaires pour atteindre le débit maximum et l'activité des cœurs est proche de la saturation.
Les graphiques correspondants :
Deux tâches | Douze tâches (débit max) |
Ces mesures permettent de constater que la limitation se situe principalement au niveau du débit descendant mais confirment également, si on en doutait encore, que la charge CPU dépend fortement du nombre de tâches en cours.
Elles permettent aussi accessoirement de constater les différences entre hébergeurs ; la qualité de leurs services. Alors qu'il ne faut que quatre tâches chez Infomaniak pour atteindre le débit maximum, douze sont nécessaires chez l'hébergeur DVP. À certaines heures, il a même fallu monter à vingt-six (je le rappelle, sur un PC bicœur) ! Le temps de téléchargement par un seul thread était alors de 499 secondes, mais… il était toujours possible de télécharger à 50 Mb/s !
Nous remarquons également que les connexions multiples ne sont d'aucune utilité chez Infomaniak si vous ne disposez que d'un accès Internet à 10 Mb/s puisque la limite de débit est supérieure à 12.
À noter encore qu'un test chez free.fr s'est révélé catastrophique ! L'impossibilité d'avoir plus de trois tâches concurrentes et un temps de latence exécrable rendent le téléchargement monothread plus rapide !
X. Conclusion▲
Nous arrivons au bout de ce tutoriel.
Le principe évoqué ici pourrait bien sûr encore être amélioré avec création des threads en fonction du débit mesuré et inclure des fonctions Suspend/Resume avec sauvegarde de l'avancement pour une reprise après redémarrage ou une plus grande tolérance aux erreurs. Un véritable Download Manager !
La source de l'unité FastURLDownload.pas est téléchargeable au format zip.
Sur ce et en espérant vous avoir intéressé, Andnotor vous souhaite le bonjour ;-)
XI. Remerciements▲
Un grand merci à ClaudeLELOUP pour la relecture orthographique.