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, pour les projets en Delphi 6, 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.
La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal,
L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La
Notation Alsacienne
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
blogs ou 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 le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) 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, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |