Utiliser GNetDate de publication : 04/09/07 , Date de mise à jour : 03/10/07
Par
Hugo Stephan (Le site de shugo)
Ce tutoriel à pour but de vous apprendre à utiliser la librairie GNet qui permet une gestion facile d'internet. Ce tutoriel explique où télécharger GNet, comment l'installé et comment l'utiliser.
I. Introduction
I-A. But de ce tutoriel
I-B. Conaissances requises
I-C. Quelques mots sur GNet
I-C-1. Qu'est-ce que GNet ?
I-C-2. Quelle est l'utilitée de GNet ?
I-C-3. Quelles applications utilisent GNet ?
I-D. Mise à jour
II. Téléchargement & Installation
II-A. Téléchargement
II-A-1. Télécharger les sources
II-A-2. Télécharger le paquet
II-B. Installation
III. Découverte
III-A. Configuration
III-A-1. Avec codeBlocks
III-B. Notre premier projet
III-C. Tour d'horizon
III-C-1. Les InetAddr
III-C-2. Les TCP sockets
III-C-3. Les UDP sockets
IV. Codes exemples
IV-A. main.c
IV-A-1. Vérification des arguments
IV-A-2. Préparation du travail
IV-A-3. Un petit résumé
IV-B. ftpget.h
IV-C. ftpget.c
IV-C-1. La fonction usage
IV-C-2. La fonction ftpget
IV-D. Test
IV-E. Améliorations
IV-E-1. main.c
IV-E-2. ftpget.h
IV-E-3. ftpget.c
V. Conclusion
V-A. Pour aller plus loin
V-B. Conclusion
V-C. Remerciements
V-D. Téléchargements
I. Introduction
I-A. But de ce tutoriel
L'objectif de ce tutoriel est de vous fournir un tutoriel alliant pratique et découverte pour commencer à utiliser GNet.
Cela passe par :
- Téléchargement
- Installation
- Découverte
- Apprentissage
I-B. Conaissances requises
Le but de ce tutoriel est de vous apprendre à utiliser correctement la librairie GNet en C, pour cela une bonne conaissance du C est requise je vous renvoie aux cours sur le C :
http://c.developpez.com/cours/
.
Biensur rien ne vous empêche d'utilisé GNet en C++.
Il serait bien que vous ayez quelques vagues conaissances de théorie réseau pour faciliter la compréhension de GNet.
Ce tutoriel s'inspire de la documentation anglophone de GNet disponible en version originale ici :
http://www.gnetlibrary.org/docs/
I-C. Quelques mots sur GNet
Je vais tenter de répondre aux questions que vous devez vous posez :
I-C-1. Qu'est-ce que GNet ?
GNet est une bibliothèque réseau utilisant les sockets mais en restant simple aux yeux des utilisateurs. GNet se base sur la Glib qu'il faudra donc installer également.
I-C-2. Quelle est l'utilitée de GNet ?
Si vous avez un jour du utiliser les sockets pour un projet quelconque, vous avez sans doute pu vous apperçevoir que cela n'est pas simple ! GNet est une librairie réseau se basant sur les sockets mais apparaissant beaucoup plus simplement aux yeux des utilisateurs.
I-C-3. Quelles applications utilisent GNet ?
Pour vous donner une idée voici une liste des principales applications utilisant GNet :
- eDonkey2000 GTK GUI
- Gnome Chinese Checkers
- Gnome Jabber
- gtermix
- Jungle Monkey
- Mail Notify
- MSI
- Pan
- PreViking
- Sussen
- Workrave
I-D. Mise à jour
- Le 05/09/2007 : Ajout de la section V-E concernant les socket multicast.
- Le 06/09/2007 : Ajout de la section V-F concernant les connexion objet HTTP.
- Le 07/09/2007 : Ajout de la section V-G concernant les objets de connexion.
- Le 07/09/2007 : ajout de la section V-H concernant les serveurs TCP.
- Le 08/09/2007 : Ajout de la section V-I concernant les GIOChannel.
- Le 08/09/2007 : Ajout de la section V-J concernant les URI.
- Le 30/09/2007 : Séparation du tutoriels en deux articles : refgnet et découverte de GNet.
- Le 03/10/2007 : Correction de la section III-A.
II. Téléchargement & Installation
II-A. Téléchargement
Il y a deux possibilitées :
- Télécharger et compiler le code source
- Télécharger et installer un paquet
II-A-1. Télécharger les sources
Pour télécharger les sources, rendez vous à l'adresse http://www.gnetlibrary.org/, le site officiel de GNet, dans la section Download, vous devriez trouvé la dernière version (actuellement la 2.0.7). Si la glib n'est pas sur votre distribution, téléchargez la (
ftp://ftp.gtk.org/pub/glib/
)
II-A-2. Télécharger le paquet
Pour les paquets, si votre distribution fourni GNet sous forme de paquet(c'est le cas des distributions récentes), installez le directement, si ce n'est pas le cas, ces liens devrait vous aider :
Si vous ne trouvez pas de paquets correspondant à votre distribution, vous pouvez toujours utilisez alien ou bien checkinstall pour créer le paquet à partir des sources. Vous aurez également besoin de la glib qui devrait normalement être automatiquement installé ccomme dépendance du paquet.
II-B. Installation
Sous Linux, si vous utilisez un gestionnaire de paquet, il n'y a pas de problème. Sinon installez les paquets classiquement en suivant l'ordre :
Si vous compilez les sources, il vous suffira classiquement de faire :
|
cd rep-source/
./configure
make
|
Puis une fois root :
Ou bien (si check install est installé)
Toujours en respectant l'ordre :
il vous faudra ensuite le paquet libgnet-dev à installer, s'il n'est pas fourni dans votre distribution, consulter les sites donnés précédement.
Maintenant que tout est installé, nous allons pouvoir commencerc les réjouissances.
III. Découverte
III-A. Configuration
A présent il faut configurer votre EDI ou votre compilateur pour pouvoir réaliser des progr mmes utilisant GNet.
pour cela, c'est très simple, il vous suffit d'ajouter cette option :
|
`pkg-config gnet-2.0 --cflags --libs`
|
Si vous compilez directement avec GCC c'est simple.
III-A-1. Avec codeBlocks
Cliquez sur New->Project, ensuite Console Application nommé votre projet GNetApp.
Ensuite, Allez dans le menu Project->Buil Options, dans l'onglet Compilers, puis dans la zone de texte du sous onglet Other Options, tapez :
`pkg-config gnet --cflags --libs`
|
Puis dans le fichier main.c tapez :
# include <stdlib.h>
# include <stdio.h>
# include <gnet.h>
int main (int argc, char * argv[])
{
printf (" Hello Word !\n " );
}
|
Si vous programmez en C++, créez un projet C++ et remplacez printf par cout puis incluez cstdlib et iostream à la place de stdlib.h et stdio.h.
Ensuite cliquez sur File->Save as user template, renseignez les informations demandés et c'est bon !
A présent lorsque vous voudrez créer un projet tout prêt configuré pour être utilisés avec GNet, il vous suffira de cliquez sur : New->Project, ensuite dans la partie User's template, cliquez sur le nom sous lequel vous avez enregistré votre projet tout à l'heure, et votre projet se créra tout prêt et configuré.
III-B. Notre premier projet
A prèsent nous allons créer notre premier projet avec GNet, si vous utilisez codeblocks créé un projet en utilisant le user's template que vous avez fait tout à l'heure, sinon rajoutez simplement :
|
`pkg-config gnet --cflags --libs`
|
dans les options du compilateur.
Pour commencer, il faut initialiser GNet en utilisant la fonction gnet_init dont voici le prototype :
Cet appel doit précéder tout appels aux fonctions de GNet et si vous utilisez GTK, cet appel doit aussi précéder l'appel à gtk_init(int argc, char** argv).
Voilà notre premier projet avec GNet !
Bon certe, il ne fait rien, mais c'est un début et tout apprentissage doit avoir un début(mais pas de fin ^^). Passons à la suite.
III-C. Tour d'horizon
Je vous propose à présent un petit tour d'horizon des possiblitées de GNet pour clore la partie découverte, après quoi nous entammerons l'apprentissage proprement dit.
III-C-1. Les InetAddr
Les InetAddr(Internet Address) représentent des adresses internet, créez une InetAddr avec :
GInetAddr* gnet_inetaddr_new (const gchar * serveur, gint port);
|
Il faut assigner la valeur de retour à un pointeur de type GInetAddr, et récupérer la valeur de retour, si c'est NULL, il y a eu une erreur :
GInetAddr* InternetAdress = gnet_inetaddr_new (const gchar * serveur, gint port);
if (InternetAdress = = NULL )
{
printf (" Erreur d'initialisation !\n " );
}
|
Nous reviendrons sur les InetAddr, mais comme il s'agit uniquement d'un tour d'horizon, nous n'allons pas nous attarder dessus.
III-C-2. Les TCP sockets
TCP est un protocole internet interne pour transférer les données de manière sur et dans le bon ordre, GNet fourni des TCP Sockets, les GTcpSocket !
Pour créer un GTcpSocket, on utilise la fonction :
GTcpSocket* gnet_tcp_socket_new (const GInetAddr* addresse);
|
Elle prend en paramètre un pointeur sur une GInetAddr que vous avez du créer précédement, de même il faut tester la valeur de retour :
const GInetAddr* InternetAdress = gnet_inetaddr_new (const gchar * serveur, gint port);
if (InternetAdress = = NULL )
{
printf (" Erreur d'initialisation !\n " );
}
GTcpSocket* TCPSocket = gnet_tcp_socket_new (InternetAddress);
if (TCPSocket = = NULL )
{
printf (" Erreur de connexion !\n " );
}
|
Une fois de plus ce n'est qu'un aperçu, nous reviendrons plus tard sur les GTcpSocket.
III-C-3. Les UDP sockets
GNet fourni aussi un support pour les sockets UDP. UDP est un protocole internet interne pour transfèrer les paquets plus rapidement, mais les paquets peuvent arriver dans le désordre(c'est même souvent le cas). Utilisez plutôt TCP si votre programme requiert une transmission de qualité et fiable.
Ce fameux support pour les Sockets UDP est GUdpSocket, pour créer un socket UDP vous avez trois fonction :
GUdpSocket* gnet_udp_socket_new (void );
GUdpSocket* gnet_udp_socket_new_with_port (gint port);
GUdpSocket* gnet_udp_socket_new_full (const GInetAddr * iface, gint port);
|
La première créé un socket UDP complétement inutile(mais nous y reviendrons), la deuxième créé un socket UDP avec un port, et la troisième créé un socket UDP complet. Il faut toujours tester la valeur de retour qui pour toutes les fonctions est NULL en cas d'erreur.
Nous reviendrons aussi sur les sockets UDP.
Ce n'étais qu'un apperçu des possibilitées de GNet, nous réetudierons tout cela plus sérieusement dans le paragraphe suivant.
IV. Codes exemples
Dans cette partie, je vous propose pour exemple de réaliser un petit programme en ligne de commande (que j'appelerais ftpget) et qui permettera de récupérer un fichier dont l'URL est donnée en argument.
IV-A. main.c
Pour commencer, codons le fichier main.c, on commence avec une structure vide :
int main (int argc, char * * argv)
{
return EXIT_SUCCESS;
}
|
On peut déjà rajouter l'inclusion du fichier d'en tête :
# include "ftpget.h"
int main (int argc, char * * argv)
{
return EXIT_SUCCESS;
}
|
Le fichier d'en tête inclus provoque surement des commentaires donc éclaircissons tout de suite les choses, ce fichier n'éxiste pas encore et nous le créerons plus tard, mais je l'inclus dès le début pour ne pas avoir à revenir sur ce fichier.
Nous allons à présent déclarer les variables que nous utiliseront (pas toujours faciles de tout prévoir...) :
- une variable pour stocker le nom du serveur de type gchar.
- une variable pour stocker le port de type gchar (car c'est sous cette forme que le port est originellement récupéré).
- une variable pour stocker le port en lui-même de type gint (après la conversion en entier).
- une variable pour stocker le nom du fichier sur le serveur de type gchar.
- une variable pour des manipulation diverses (récupération d'argument, etc...) de type gchar.
Du côté du code on en est là... | # include "ftpget.h"
int main (int argc, char * * argv)
{
gchar* serveur = NULL ;
gchar* portStr = NULL ;
gint port = 80 ;
gchar* filename = NULL ;
gchar* p = NULL ;
return EXIT_SUCCESS;
}
|
 |
Attention, prennez l'habitude de toujours initialiser vos variables !
|
IV-A-1. Vérification des arguments
Il nous faut à présent vérifier les arguments donnés à notre programme :
Le nombre d'argument (variable argc) ne doit pas être différent de 2 (il faut prendre en compte le nom de notre programme qui est le numéro 1.
|
Sinon, on rappel à l'utilisateur la façon dont il doit utiliser notre programme, on peut être tenté d'écrire le rappel tel quel à grand coup de printf, mais il est fort possible, que nous soyons amenés à afficher plusieurs fois (et depuis des endroits très différents) ce même rappel, c'est pourquoi il vaudrait mieu le coder dans une fonction à part (que nous verrons lorsque nous écrirons le fichier ftpget.c), en attendant, contentons-nous de ceci :
if (argc ! = 2 )
{
usage ();
exit (EXIT_FAILURE);
}
|
 |
Attention, n'oubliez pas de quitter le programme dans de telles circonstances sans quoi cela pourrait entraîner des bogues très fâcheux.
|
IV-A-2. Préparation du travail
Pour correctement répartir les tâches, il nous reste encore à préparer un peu le travail avant d'appeler la fonction ftpget (que nous verrons dans le fichier ftpget.c).
Cela passe par :
- Initialisation de GNet.
- Récupération des arguments.
- Extraction d'un préfixe éventuel.
- Récupération (purifiée) du nom du serveur.
- Récupération du numéro de port.
- Récupération du nom du fichier sur le serveur.
- Appel de la fonction ftpget.
Bon, au travail !
L'initialisation de GNet, ça devrait pas poser trop de problème :
La récupération des arguments non plus (nous allons utiliser notre fameuse variable p) :
L'extraction du préfixe devrait être un peu plus coriace...
L'idée est simple :
Si la comparaison entre "http://" et p limitée à (sizeof ("http://") - 1) caractères est juste (donc s'il y a un suffixe), on tronque purement est simplement la partie suffixe, il ne nous reste plus qu'à reporduire la même chose pour le suffixe "ftp://". Je vous épargne des peines, je vous donne le code :
if (strncmp (" http:// " , p, sizeof (" http:// " ) - 1 ) = = 0 )
{
p = & p[sizeof (" http:// " ) - 1 ];
}
if (strncmp (" ftp:// " , p, sizeof (" ftp:// " ) - 1 ) = = 0 )
{
p = & p[sizeof (" ftp:// " ) - 1 ];
}
|
La récupération purifié du nom d'hôte permet de séparer le nom du serveur du chemin sur le serveur, par exemple :
Dans http://www.developpez.com/test/test.txt:21 :
http:// est le préfixe.
www.developpez.com est le nom du serveur.
/test/test.txt est le chemin sur le serveur
:21 indique le numéro de port
|
Pour récupérer le nom purifié du serveur, on parcourt p et on s'arrête dès que l'on rencontre un "/" ou un ":", je vous donne le code :
serveur = p;
while (* p ! = 0 & & * p ! = ' : ' & & * p ! = ' / ' )
{
+ + p;
}
if ((p - serveur) = = 0 )
{
usage ();
exit (EXIT_FAILURE);
}
serveur = g_strndup (serveur, p - serveur);
|
La récupération du numéro de port est simple : si il y a un ":" c'est que le numéro de port est indiqué, sinon il reste à la valeur par défaut que nous n'avons pas oublié de lui mettre, c.à.d 80.
if (* p = = ' : ' )
{
portStr = + + p;
while (* p ! = 0 & & * p ! = ' / ' ) + + p;
if ((p - portStr) = = 0 ) { usage (); exit (EXIT_FAILURE); }
portStr = g_strndup (portStr, p - portStr);
port = atoi (portStr);
}
|
Pour récupérer le nom du fichier sur le serveur ce n'est pas bien compliqué non plus : s'il n'y a pas de / on récupére la racine, sinon on récupére le nom de fichier qui suit le premier "/" :
if (* p = = 0 )
filename = g_strdup (" / " );
else
filename = g_strdup (p);
|
Et enfin l'appel de la fonction ftpget, bien que nous n'avons pas défini le prototype, vous devez déjà l'imaginer, c'est celui-ci :
ftpget (serveur, port, filename);
|
Et voilà, on a terminé le fichier main.c, on va passer au fichier ftpget.h si vous le voulez bien.
IV-A-3. Un petit résumé
Je ne suis pas persuadé qu'un petit résumé soit superflu...
main.c |
# include "ftpget.h"
int main (int argc, char * * argv)
{
gchar* serveur = NULL ;
gchar* portStr = NULL ;
gint port = 80 ;
gchar* filename = NULL ;
gchar* p = NULL ;
gnet_init ();
if (argc ! = 2 )
{
usage ();
exit (EXIT_FAILURE);
}
p = argv[1 ];
if (strncmp (" http:// " , p, sizeof (" http:// " ) - 1 ) = = 0 )
{
p = & p[sizeof (" http:// " ) - 1 ];
}
serveur = p;
while (* p ! = 0 & & * p ! = ' : ' & & * p ! = ' / ' )
{
+ + p;
}
if ((p - serveur) = = 0 )
{
usage ();
exit (EXIT_FAILURE);
}
serveur = g_strndup (serveur, p - serveur);
if (* p = = ' : ' )
{
portStr = + + p;
while (* p ! = 0 & & * p ! = ' / ' )
{
+ + p;
}
if ((p - portStr) = = 0 )
{
usage ();
exit (EXIT_FAILURE);
}
portStr = g_strndup (portStr, p - portStr);
port = atoi (portStr);
}
if (* p = = 0 )
filename = g_strdup (" / " );
else
filename = g_strdup (p);
ftpget (serveur, port, filename);
g_free (serveur);
g_free (portStr);
g_free (filename);
return EXIT_SUCCESS;
}
|
IV-B. ftpget.h
Il nous faut maintenant coder le fichier ftpget.h qui doit contenir les prototypes des deux fonctions :
Pour ce qui est du prototype de la fonction usage, rien de plus simple :
Par contre réfléchissons un peu pour la fonction ftpget, voici les arguments dont nous aurons besoin :
- Le nom "purifié" du serveur.
- Le numéro de port (tant qu'a faire autant passer celui de type gint).
- Le nom du fichier sur le serveur.
Donc voici le prototype que l'on pourrait faire :
void ftpget (gchar* serveur, gint port, gchar* filename);
|
 |
Attention, n'oubliez pas la vérification anti inclusion infinie !
|
Au final voilà le code du fichier ftpget.h :
ftpget.h | # ifndef FTPGET_H_INCLUDED
# define FTPGET_H_INCLUDED
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <glib.h>
# include <gnet.h>
void usage (void );
void ftpget (gchar* serveur, gint port, gchar* filename);
# endif / / FTPGET_H_INCLUDED
|
IV-C. ftpget.c
C'est dans ce fichier que se trouvera la majeur partie du programme, mais ne vous inquiètez pas, nous avons suffisament décomposé le programme pour ne pas en faire une fonction illisible.
Commençons pas les inclusions du fichier d'en tête :
IV-C-1. La fonction usage
Pour ne pas être découragé tout de suite, commençons par la fonction usage qui ne fait qu'un très simple appel à la fonction printf :
La fonction usage | void usage (void )
{
printf
(" Utilisation: ftpget url\nExemple : ftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt\n " );
}
|
IV-C-2. La fonction ftpget
C'est LE gros bout du programme, mais on va y allait doucement, il ne devrait pas y avoir de problème.
Commençons par les déclarations de variables.
Il nous faut :
- Une variable de type GInetAddr.
- Une socket de type GTcpSocket.
- Un canal d'E/S de type GIOChannel.
- Une variable pour stocker la commande de type gchar.
- Un buffer de type gchar.
- Une variable pour tester les erreurs de type GIOError.
- Une variable pour contenir le nombre d'octet écrit.
Donc voici le code de déclaration de variables :
GInetAddr* iAddr;
GTcpSocket* socket;
GIOChannel* ioChannel;
gchar* commande;
gchar buffer[1024 ];
GIOError error;
guint n;
|
Ensuite, voici les principales étapes :
- Création de la GInetAddr.
- Création et connexion de la socket.
- Récupération du canal d'E/S.
- Préparation et envoie de la commande.
- Test d'état du canal.
- Lecture du fichier.
- Suppression de la socket.
Au travail !
Pour la création de la GInetAddr, rien de plus facile :
iAddr = gnet_inetaddr_new (serveur, port);
g_assert (iAddr ! = NULL );
|
 |
Attention, pensez (comme ici) à vérifier que la création de variables se soient bien fait, par exemple à l'aide d'assertions.
|
La création et la connexion de la socket ne pose pas trop de problème normalement :
socket = gnet_tcp_socket_new (iAddr);
gnet_inetaddr_delete (iAddr);
g_assert (socket ! = NULL );
|
 |
Attention, n'oubliez pas de supprimer la GInetAddr qui ne sert plus à rien après la création de la socket TCP.
|
Pour la récupération du canal d'Entrée/Sortie, rien d'infaisable non plus :
ioChannel = gnet_tcp_socket_get_io_channel (socket);
g_assert (ioChannel ! = NULL );
|
Un peu plus dur maintenant ! Il nous faut préparer la commande que nous allons envoyer (pour un résumé des commandes FTP consultez cette page). Notre commande sera celle-ci :
Une simple concaténation devrait suffit, ensuite on écrit le tout dans la canal d'Entrée/Sortie, sans oublier de libérer la mémoire de la variable commande.
commande = g_strconcat (" GET " , filename, " \n " , NULL );
error = gnet_io_channel_writen (ioChannel, commande, strlen (commande), & n);
g_free (commande);
|
Il nous faut ensuite tester l'état du canal d'E/S :
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
}
|
 |
Prennez l'habitude de tester l'état des canaux d'E/S après chaque action effectuée avec.
|
Il nous faut maintenant lire la réponse du serveur toujours via le canal d'entrée sortie. C'est pas très compliqué, dans une boucle infinie (while (1)) on lit 1024 caractères du résultat et on les affiche, jusqu'à ce qu'il n'y ai plus rien à lire auquel ca on sort de la boucle while.
while (1 )
{
error = g_io_channel_read (ioChannel, buffer, sizeof (buffer), & n);
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
break ;
}
if (n = = 0 )
break ;
fwrite (buffer, n, 1 , stdout);
}
|
Ne nous arrêtons pas en si bon chemin, n'oublions pas de supprimer la socket TCP :
gnet_tcp_socket_delete (socket);
|
Voilà nous avons finit, un petit résumé du fichier ftpget.c ?
# include "ftpget.h"
void usage (void )
{
printf
(" Utilisation: ftpget url\nExemple : ftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt\n "
" Vous pouvez aussi récupérer le tout dans un fichier séparé en spécifiant une sortie (avec l'option "
" -o), exemple :\nftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt -o test.txt\n " );
}
void ftpget (gchar* serveur, gint port, gchar* filename)
{
GInetAddr* iAddr;
GTcpSocket* socket;
GIOChannel* ioChannel;
gchar* commande;
gchar buffer[1024 ];
GIOError error;
guint n;
iAddr = gnet_inetaddr_new (serveur, port);
g_assert (iAddr ! = NULL );
socket = gnet_tcp_socket_new (iAddr);
gnet_inetaddr_delete (iAddr);
g_assert (socket ! = NULL );
ioChannel = gnet_tcp_socket_get_io_channel (socket);
g_assert (ioChannel ! = NULL );
commande = g_strconcat (" GET " , filename, " \n " , NULL );
error = gnet_io_channel_writen (ioChannel, commande, strlen (commande), & n);
g_free (commande);
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
}
while (1 )
{
error = g_io_channel_read (ioChannel, buffer, sizeof (buffer), & n);
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
break ;
}
if (n = = 0 )
break ;
fwrite (buffer, n, 1 , stdout);
}
gnet_tcp_socket_delete (socket);
}
|
IV-D. Test
Pour compiler le programme créé, tapez cette ligne (pour GCC, adaptez celon votre compilateur) :
gcc -o ftpget `pkg-config --cflags --libs gnet-2.0` ftpget.c `pkg-config --cflags --libs gnet-2.0` main.c
|
A présent vous pouvez récupérer un fichier sur internet, par exemple :
[shugo@myLinux~ :] ./ftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt
Essai de ftpget.
|
Vous remarquerez que le fichier téléchargé s'affiche directement sur la sortie standard, c'est rarement le comportement voulu, c'est pour cela que nous allons revoir notre programme pour permettre à l'utilisateur de rediriger la sortie vers un fichier (je précise que c'est déjà faisable en utilisant > vers un fichier).
IV-E. Améliorations
Notre but dans cette partie va de permettre à l'utilisateur de rediriger la sortie vers un fichier avec l'option -o, et si cela n'est pas fait nous redirigons automatiquement la sortie vers le fichier ftpget-out.txt. Pour cela nous allons devoir revoir quelques points.
IV-E-1. main.c
Il y a tout d'abord quelques points à changer dans le fichier main.c, pour commencer, il nous faut changer la détection d'argument : le nombre d'argument doit être d'au moins 2, inférieur à 4, et s'il y en a 4 le 3 ème doit être -o, sans quoi on appel la fonction usage (), voici ce que donne le code :
if (argc < 2 )
{
usage ();
exit (EXIT_FAILURE);
}
if (argc > 4 )
{
usage ();
exit (EXIT_FAILURE);
}
if (argc = = 2 )
{
outFilename = g_strdup (" ftpget-out.txt " );
}
else if (g_strcasecmp (argv [2 ], " -o " ) = = 0 & & argc = = 4 )
{
outFilename = g_strdup (argv[3 ]);
}
else
{
usage ();
exit (EXIT_SUCCESS);
}
|
On doit également rajouter la variable outFilename qui est de type gchar*, qui est utilisé dans l'assignement.
 |
Attention, n'oubliez pas de libérer la mémoire de outFilename !
|
Il nous faut régler un dernier détail avant d'en finir avec main.c, il nous faut rajouter l'argument outFilename dans l'appel à ftpget (on modifieras donc également son prototype). Voici la nouvelle version du fichier main.c :
main.c |
# include "ftpget.h"
int main (int argc, char * * argv)
{
gchar* serveur = NULL ;
gchar* portStr = NULL ;
gint port = 80 ;
gchar* filename = NULL , * outFilename = NULL ;
gchar* p = NULL ;
gnet_init ();
if (argc < 2 )
{
usage ();
exit (EXIT_FAILURE);
}
if (argc > 4 )
{
usage ();
exit (EXIT_FAILURE);
}
if (argc = = 2 )
{
outFilename = g_strdup (" ftpget-out.txt " );
}
else if (g_strcasecmp (argv [2 ], " -o " ) = = 0 & & argc = = 4 )
{
outFilename = g_strdup (argv[3 ]);
}
else
{
usage ();
exit (EXIT_SUCCESS);
}
p = argv[1 ];
if (strncmp (" http:// " , p, sizeof (" http:// " ) - 1 ) = = 0 )
{
p = & p[sizeof (" http:// " ) - 1 ];
}
serveur = p;
while (* p ! = 0 & & * p ! = ' : ' & & * p ! = ' / ' ) + + p;
if ((p - serveur) = = 0 ) { usage (); exit (EXIT_FAILURE); }
serveur = g_strndup (serveur, p - serveur);
if (* p = = ' : ' )
{
portStr = + + p;
while (* p ! = 0 & & * p ! = ' / ' ) + + p;
if ((p - portStr) = = 0 ) { usage (); exit (EXIT_FAILURE); }
portStr = g_strndup (portStr, p - portStr);
port = atoi (portStr);
}
if (* p = = 0 )
filename = g_strdup (" / " );
else
filename = g_strdup (p);
ftpget (serveur, port, filename, outFilename);
g_free (serveur);
g_free (portStr);
g_free (filename);
g_free (outFilename);
return EXIT_SUCCESS;
}
|
IV-E-2. ftpget.h
Ce fichier est une fois de plus le plus simple, il nous suffit de modifier le prototype de ftpget pour y ajouter outFilename, voici le fichier au complet :
ftpget.h | # ifndef FTPGET_H_INCLUDED
# define FTPGET_H_INCLUDED
void usage (void );
void ftpget (gchar* serveur, gint port, gchar* filename, gchar* outFilename);
# endif / / FTPGET_H_INCLUDED
|
IV-E-3. ftpget.c
Commencons par modifier la fonction usage :
void usage (void )
{
printf
(" Utilisation: ftpget url\nExemple : ftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt\n "
" Vous pouvez aussi récupérer le tout dans un fichier séparé en spécifiant une sortie (avec l'option "
" -o), exemple :\nftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt -o test.txt\n " );
}
|
En ce qui concerne la fonction ftpget, voici la liste de ce qui doit être modifié :
- Ajout d'une variable de type FILE*.
- Ouverture du fichier outFilename.
- Ecriture de la sortie dans le fichier outFilename via la variable de type FILE* au lieu d'écrire dans la sortie standard.
- Fermeture du fichier.
Ajoutons donc notre variable de type FILE* :
On ouvre ensuite le fichier :
outFile = fopen (outFilename, " w " );
g_assert (outFile ! = NULL );
|
Et pour finir, on écrit dans le fichier (via le descripteur de fichier outFile) au lieu d'écrire sur la sortie standard, pour cela, on remplace cette ligne :
fwrite (buffer, n, 1 , stdout);
|
par celle-ci :
fwrite (buffer, n, 1 , outFile);
|
Et on oublie pas la fermeture du fichier :
Pour résumer voici le nouveau fichier ftpget.c :
ftpget.c | # include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <gnet.h>
# include "ftpget.h"
void usage (void )
{
printf
(" Utilisation: ftpget url\nExemple : ftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt\n "
" Vous pouvez aussi récupérer le tout dans un fichier séparé en spécifiant une sortie (avec l'option "
" -o), exemple :\nftpget ftp-developpez.com/shugo/exemple/ftpget/try.txt -o test.txt\n " );
}
void ftpget (gchar* serveur, gint port, gchar* filename, gchar* outFilename)
{
GInetAddr* iAddr;
GTcpSocket* socket;
GIOChannel* ioChannel;
gchar* commande;
gchar buffer[1024 ];
GIOError error;
guint n;
FILE* outFile;
iAddr = gnet_inetaddr_new (serveur, port);
g_assert (iAddr ! = NULL );
socket = gnet_tcp_socket_new (iAddr);
gnet_inetaddr_delete (iAddr);
g_assert (socket ! = NULL );
ioChannel = gnet_tcp_socket_get_io_channel (socket);
g_assert (ioChannel ! = NULL );
commande = g_strconcat (" GET " , filename, " \n " , NULL );
error = gnet_io_channel_writen (ioChannel, commande, strlen (commande), & n);
g_free (commande);
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
}
outFile = fopen (outFilename, " w " );
g_assert (outFile ! = NULL );
while (1 )
{
error = g_io_channel_read (ioChannel, buffer, sizeof (buffer), & n);
if (error ! = G_IO_ERROR_NONE)
{
g_warning (" Erreur: %d\n " , error);
break ;
}
if (n = = 0 )
break ;
fwrite (buffer, n, 1 , outFile);
}
fclose (outFile);
gnet_tcp_socket_delete (socket);
}
|
Vous pouvez maintenant rediriger les sorties vers un autre fichier grâce à l'option -o, la procédure de compilation n'a pas changée.
V. Conclusion
V-A. Pour aller plus loin
V-B. Conclusion
GNet est une bibliothèque réseau très pratique et très puissant. La logique de la librairie GNet est identique à celle de la glib ou de GTK+ :
Cela permet de retrouver facilement des noms de fonctions. De plus GNet utilise des principes objets tout comme la glib et GTK+ encore une fois. Il reste encore quelques parties mineures que je n'ai pas traité, vous pouvez trouvé de la doc grâce au lien précédement donné.
Pour toutes corrections ou erreurs, contactez-moi par MP.
V-C. Remerciements
Je tiens à remercier gege2061 pour son autorisation à faire cet article et pour son aide lors de mes débuts. Ainsi que gorgonite pour son soutien et son aide précieuse.
V-D. Téléchargements
Les sources de la dernière version du programme sont disponibles ici.
Les sources présentés sur cette pages sont libre de droits,
et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright ©2008 Hugo Stephan.
Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu :
textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérets.
Cette page est déposée à la SACD.
|