|
Client HTTP - John COLIBRI.
|
- résumé : comment télécharger une page .HTML en utilisant le tClientSocket
de Delphi
- mots clé : page .HTML - protocole HTTP - tClientSocket - programmation
Socket Windows - GET - diagramme de classe UML / sockets
- logiciel utilisé : Windows XP personnel, Delphi 6.0
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque
dur
- champ d'application : Delphi 1 à 2006, Turbo Delphi sur Windows
- niveau : développeur Delphi - Internet
- plan :
1 - Le protocole HTTP
Nous allons présenter un projet permettant de télécharger une page Internet en
utilisant le socket client, tClientSocket, fourni avec Delphi.
Depuis plus d'une décennie, de nombreuses solutions ont été proposées pour
télécharger une page Internet en utilisant Delphi. Pour n'en citer que
quelques unes
- Socket Windows
- WinInet
- les composants NetManage (abandonnés par Delphi depuis Delphi 5)
- les composants Indy (en vogue depuis Delphi 6
- les composants ICS, ou les autres librairies TCP IP (Synapse etc)
Nous allons ici utiliser le composant tClientSocket fourni avec Delphi 6
Nous avons présenté plusieurs articles pour effectuer des transferts TCP-IP:
- Programmation WinSocket : communication
Client Server utilisant les API de la librairie WinSock. Le plus simple
exemple de programmation socket en Delphi
- architecture des composants Socket
Delphi : l'organisation des classes de l'unité ScktComp, avec les
diagrammes de classe UML et un exemple simple de transfert de fichiers
utilisant tClientSocket et tServerSocket (le tClientSocket étant le
composant utilisé dans cet article)
Nous organisons aussi régulièrement des formations TCP /IP:
2 - Fonctionnement HTTP
Pour télécharger une page, il faut:
- se connecter à l'aide de notre tClientSocket au serveur distant
- envoyer une requête ayant un format spécifique
- lire les données envoyées par le Serveur.
Voici le détail des opérations pour charger, par exemple, la page "Formation
Turbo Delphi" du site "Formation-Delphi"
- sur le site où est hébergée la page, le Serveur HTTP attend les requêtes:
- le Client HTTP se connecte à ce serveur:
- le Serveur accepte la connection
- le Client HTTP envoie la demande de la page de formation:
- le Serveur envoie, en un ou plusieurs paquets, le contenu de la page:
3 - Le Projet Delphi de téléchargement HTML
3.1 - L'utilisation directe du tClientSocket
|
créez un nouveau projet Delphi
|
|
de l'onglet "Internet", sélectionnez le tClientSocket et posez-le sur la
Forme
|
|
créez son événement OnConnect, qui sera appelé lorsque la connection aura
été établie. C'est là que nous avons choisi d'envoyer la requête HTTP Get,
après avoir préparé le tampon de réception:
var g_c_reception_buffer: c_byte_buffer= Nil;
procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
var l_page: String;
l_get_request: String;
begin
// -- send the HTTP get
l_page:= page_edit_.Text;
l_get_request:= 'GET '+ l_page+ ' HTTP/1.0'+ k_new_line
+ 'Accept: image/gif, image/x-xbitmap, image/jpeg, '
+ ' image/pjpeg, */*'+ k_new_line
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; '
+ ' Windows NT 5.1)'+ k_new_line
+ 'Host: '+ host_edit_.Text+ k_new_line
+ k_new_line;
g_c_reception_buffer.Free;
g_c_reception_buffer:= c_byte_buffer.create_byte_buffer('reception',
2* 1024);
ClientSocket1.Socket.SendBuf(Pointer(l_get_request)^,
Length(l_get_request));
end; // ClientSocket1Connect
|
|
|
créez son événement OnRead. Cet événement sera appelé chaque fois que le
Client reçoit un paquet du Serveur. Nous stockons ces données dans le
tampon g_c_reception_buffer
procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var l_bytes_received: Integer;
l_remaining: Integer;
l_pt_start_reception: Pointer;
begin
with g_c_reception_buffer do
begin
repeat
// -- add the received data to the current buffer
l_remaining:= m_buffer_size- m_write_index;
// -- if not at least a tcp-ip chunk, increase the room
if l_remaining< k_tcp_ip_chunk
then begin
double_the_capacity;
l_remaining:= m_buffer_size- m_write_index;
end;
l_pt_start_reception:= @ m_oa_byte_buffer[m_write_index];
// -- get at most k_tcp_ip_chunk
l_bytes_received:= ClientSocket1.Socket.ReceiveBuf(l_pt_start_reception^,
k_tcp_ip_chunk);
if l_bytes_received<= 0
then begin
display(' ...no_more_for_now');
Break;
end;
if l_bytes_received> 0
then Inc(m_write_index, l_bytes_received);
until False;
end; // with g_c_reception_buffer
end; // ClientSocket1Read
|
|
|
créez son événement OnDisconnect. C'est là que nous exploitons les données
reçues (sauvegarde, analyse etc):
procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
with g_c_reception_buffer do
save_to_file_start_end(f_exe_path+ '..\_data\resu.html',
f_next_2_new_line_position(0)+ 4, m_write_index- 1);
end; // ClientSocket1Disconnect
|
|
|
posez un tButton sur la Forme, créez son événement Click et tapez le
code qui va lancer la requête:
procedure TForm1.connect_Click(Sender: TObject);
begin
with ClientSocket1 do
begin
Close();
Host:= host_edit_.Text;
Port:= 80;
Open();
end;
end; // with ClientSocket1
|
|
|
compilez, executes, cliquez "connect_"
|
|
voici le résultat:

|
Notez que
- comme indiqué dans l'article sur les
Sockets Delphi, c'est tClientSocket.Socket qui est le véritable
WinSocket, alors que tClientSocket n'est qu'une mince encapsulation objet
de ce WinSocket.
- comment avons nous déterminé les paramètres de GET HTTP ? Nous avions
commencé par lire les spécifications, et comme cela ne marchait pas
toujours, nous avons simplement envoyé une requête simple en utilisant
Internet Explorer, mais en ayant branché auparavant le
Sniffer TCP / IP qui nous a révélé le contenu
exact des paquets envoyés entre Internet Explorer et le Serveur. Nous
avons ensuite copié ces paramètres (Mozilla, Compatible etc) sans autre état
d'âme
3.2 - La classe c_http_client
Pour pouvoir rapidement mettre en oeuvre le téléchargement depuis d'autres
applications (la lecture de blogs en l'occurence), nous avons encapsulé le
tClientSocket dans une CLASSe dont voici la définition:
c_http_client= class; // Forward
t_po_received_event= Procedure(p_c_http_client: c_http_client) of Object;
c_http_client=
class(c_basic_object)
m_c_client_socket: tClientSocket;
m_url: String;
m_c_reception_buffer: c_byte_buffer;
m_total_received_bytes: Integer;
m_on_received_event: t_po_received_event;
Constructor create_http_client(p_name: String);
procedure handle_socket_error(p_c_client_socket: tObject;
p_c_winsocket: TCustomWinSocket;
p_error_event: TErrorEvent; var pv_error_code: Integer);
procedure connect;
procedure handle_after_connection(p_c_client_socket: tObject;
p_c_winsocket: TCustomWinSocket);
procedure handle_write(p_c_client_socket: tObject;
p_c_winsocket: TCustomWinSocket);
procedure handle_read(p_c_client_socket: tObject;
p_c_winsocket: TCustomWinSocket);
procedure disconnect;
procedure handle_after_disconnection(p_c_client_socket: tObject;
p_c_winsocket: TCustomWinSocket);
procedure download_page(p_url: String;
p_po_received_event: t_po_received_event);
Destructor Destroy; Override;
end; // c_http_client
|
Notez que:
- l'événement m_on_received_event que nous utiliserons pour prévenir
l'utilisateur de cette CLASSe que le téléchargement est terminé
- nous avons modifié le nom des événements pour nous aligner sur la convention
Alsacienne (p_c_xxx etc). Nous aurions pu utiliser Sender et Socket.
Le constructeur créé simplement le tClientSocket (la même chose qui était
effectuée en posant le composant sur le Forme depuis la Palette, et
initialise le tampon de réception):
Constructor c_http_client.create_http_client(p_name: String);
begin
Inherited create_basic_object(p_name);
m_c_reception_buffer:= c_byte_buffer.create_byte_buffer('receive',
2* k_tcp_ip_chunk);
m_c_client_socket:= tClientSocket.Create(Nil);
with m_c_client_socket do
begin
Port:= k_http_port;
OnError:= handle_socket_error;
OnConnect:= handle_after_connection;
OnWrite:= handle_write;
OnRead:= handle_read;
OnDisconnect:= handle_after_disconnection;
end; // with m_c_client_socket
end; // create_http_client
|
Et lorsque le Serveur fermera la connection, OnDisconnect sera appelé, et
cette méthode appellera la procédure de l'utilisateur de la CLASSe:
procedure c_http_client.handle_after_disconnection(
p_c_client_socket: tObject; p_c_winsocket: TCustomWinSocket);
begin
if Assigned(m_on_received_event)
then m_on_received_event(Self);
end; // handle_after_disconnection
|
Au niveau de la Forme principale, nous avons aussi ajouté (le tout figurant
dans le .ZIP des sources ci-dessous):
- une chargement dans une tListBox d'un fichier contenant plusieurs URL
- la possibilité de visualiser le contenu du fichier .HTML téléchargé
3.3 - UML: Diagramme de Classe
Nous pouvons représenter la structure de notre mécanique par le diagramme de
classe UML suivant
3.4 - UML: Diagramme de Séquence
Et pour visualiser "qui appelle qui", ainsi que les interactions au niveau des
classes, voici le diagrame de séquence UML:
3.5 - Mini Manuel
Pour utiliser la version avec la CLASSe c_http_client
- remplissez un fichier avec des URLs à télécharger
- compilez et exécutez
- dans l'onglet "dir_" spécifiez le fichier des URLs
- dans l'onglet "download_"
- cliquez "load_urls_" si la tListBox n'est pas déjà chargée
- cliquez sur la page à télécharger
- cliquez "save_" pour sauvegarder le fichier
4 - Améliorations
Ce programme est sans prétention. De nombreux points peuvent être mentionnés
- les composants ICS, Indy ou autre automatisent entièrement le processus de
mise en forme de la requête, et de récupération du fichier
- à l'extrême limite du spectre, nous pouvons même utiliser le tWebBrowser,
qui permet d'inclure un ActiveX Internet Explorer dans notre application.
Ce qui n'est pas sans difficultés pour manipuler le contenu obtenu, et qui
explique notre choix pour le tClientSocket. Mais pour le téléchargement
rapide et lorsqu'il faut uniquement affichage graphiquement le contenu, le
tWebBrowser reste imbattable, et nous l'utiliserons pour afficher le
contenu de blogs.
5 - Télécharger le code source Delphi
Vous pouvez télécharger:
Ce .ZIP qui comprend:
- le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque
.ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent:
- créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre,
de DLL ou autre). Pour supprimer le projet, effacez le répertoire.
Comme d'habitude:
- nous vous remercions de nous signaler toute erreur, inexactitude ou
problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion
d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre
e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos réponses
sur les messageries. C'est très simple: plus nous aurons de visiteurs et de
références Google, plus nous écrirons d'articles.
6 - L'auteur
John COLIBRI est passionné par le développement
Delphi et les applications de Bases de Données. Il a écrit de nombreux livres
et articles, et partage son temps entre la
développement de projets pour
ses clients, le conseil et la formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des
stages
de formation Delphi, base de données, Ado.Net, Asp.Net et UML qu'il
anime personellement tous les mois, à Paris, en province ou sur site client.
|