Accueil
Accueil Le Club Delphi Kylix C C++ Java J2EE DotNET & C# Visual Basic Access Pascal Dev Web PHP ASP XML UML SQLSGBD Windows Linux Autres

Utiliser GNet

Date 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 :

  1. Téléchargement
  2. Installation
  3. Découverte
  4. 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 : en 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 :

  • glib
  • GNet
  • GNet-dev
Si vous compilez les sources, il vous suffira classiquement de faire :

cd rep-source/
./configure
make
Puis une fois root :

makeinstall
Ou bien (si check install est installé)

checkinstall
Toujours en respectant l'ordre :

  • glib
  • GNet
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 :

void gnet_init();
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)
{
	/* déclaration des variables */
	gchar* serveur = NULL;
    	gchar* portStr = NULL;
    	gint port = 80;
    	gchar* filename = NULL;
    	gchar* p = NULL;
    
	return EXIT_SUCCESS;
}
warning 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);
}
warning 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 :
gnet_init ();
La récupération des arguments non plus (nous allons utiliser notre fameuse variable p) :
p = argv[1];
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 :
/* extraction du préfixe http:// */
if (strncmp ("http://", p, sizeof ("http://") - 1) == 0)
{
    p = &p[sizeof ("http://") - 1];
}
/* extraction du préfixe ftp:// */
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 :
// on assigne le serveur à p
serveur = p;
// on parcourt p en s'arrêtant dès que l'on rencontre un "/" (début du chemin sur le serveur) 
// ou un ":" (indication du numéro de port)
while (*p != 0 && *p != ':' && *p != '/') 
{
	++p;
}
// s'il n'y a rien à part ça, on appel la fonction usage () 
if ((p - serveur) == 0) 
{
	usage ();
	exit (EXIT_FAILURE);
}

// sinon on assigne le serveur à lui même ôté de p
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.
/* récupération du port */
if (*p == ':')
{
	portStr = ++p;
    	while (*p != 0 && *p != '/') ++p;
    	if ((p - portStr) == 0) { usage(); exit (EXIT_FAILURE); }
        	portStr = g_strndup (portStr, p - portStr);

    	/* on convertit la chaîne correspondant au port en entier */
    	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 "/" :
/* par défaut on récupére la racine du serveur */
if (*p == 0)
	filename = g_strdup ("/");
/* sinon on récupére le nom du fichier */
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
/* Programme d'exemple ftpget :
 * permet la récupération d'un fichier dont l'URL est donnée en argument.
 */

#include "ftpget.h"

int main (int argc, char** argv)
{
	/* déclaration des variables */
    	gchar* serveur = NULL;
    	gchar* portStr = NULL;
    	gint port = 80;
    	gchar* filename = NULL;
    	gchar* p = NULL;

    	/* initialisation de GNet */
    	gnet_init ();

    	/* on vérifie les arguments */
    	if (argc != 2)
    	{
        	usage ();
	        exit (EXIT_FAILURE);
    	}

    	/* on récupére les arguments */
    	p = argv[1];

    	/* extraction du préfixe */
    	if (strncmp ("http://", p, sizeof ("http://") - 1) == 0)
    	{
	        p = &p[sizeof ("http://") - 1];
    	}

    	/* on récupére le nom du serveur */
    	serveur = p;
    	while (*p != 0 && *p != ':' && *p != '/')
	{
		++p;
	}
        if ((p - serveur) == 0) 
	{ 
		usage (); 
		exit (EXIT_FAILURE); 
	}
	serveur = g_strndup (serveur, p - serveur);

	/* récupération du port */
    	if (*p == ':')
    	{
        	portStr = ++p;
        	while (*p != 0 && *p != '/') 
		{
			++p;
		}
        	if ((p - portStr) == 0) 
		{ 
			usage (); 
			exit (EXIT_FAILURE); 
		}
            	portStr = g_strndup (portStr, p - portStr);

        	/* on convertit le port en int */
        	port = atoi (portStr);
    	}

    	/* par défaut on récupére la racine du serveur */
    	if (*p == 0)
        filename = g_strdup ("/");

    	/* sinon on récupére le nom du fichier */
    	else
        	filename = g_strdup (p);

    	/* on récupére le fichier */
    	ftpget (serveur, port, filename);

    	/* libération de la mémoire */
    	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 :

  • ftpget
  • usage
Pour ce qui est du prototype de la fonction usage, rien de plus simple :
void usage (void);
Par contre réfléchissons un peu pour la fonction ftpget, voici les arguments dont nous aurons besoin :

  1. Le nom "purifié" du serveur.
  2. Le numéro de port (tant qu'a faire autant passer celui de type gint).
  3. Le nom du fichier sur le serveur.
Donc voici le prototype que l'on pourrait faire :
void ftpget (gchar* serveur, gint port, gchar* filename);
warning 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>

/* prototypes des fonctions */
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 :
#include "ftpget.h"

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);
warning 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);
warning 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 fr cette page). Notre commande sera celle-ci :
GET fichier\n
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);
}
idea 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"

/* on rappel l'utilisation de ftpget */
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)
{
    /* déclaration des variables */
    GInetAddr* iAddr;
    GTcpSocket* socket;
    GIOChannel* ioChannel;
    gchar* commande;
    gchar buffer[1024];
    GIOError error;
    guint n;

    /* Création de la inetAddr */
    iAddr = gnet_inetaddr_new (serveur, port);
    g_assert (iAddr != NULL);

    /* Création de la socket TCP */
    socket = gnet_tcp_socket_new (iAddr);
    gnet_inetaddr_delete (iAddr);
    g_assert (socket != NULL);

    /* Récupération du canal */
    ioChannel = gnet_tcp_socket_get_io_channel (socket);
    g_assert (ioChannel != NULL);

    /* Préparation et envoie de la commande */
    commande = g_strconcat ("GET ", filename, "\n", NULL);
    error = gnet_io_channel_writen (ioChannel, commande, strlen (commande), &n);
    g_free (commande);

    /* S'il y a une erreur */
    if (error != G_IO_ERROR_NONE)
    {
        g_warning ("Erreur: %d\n", error);
    }

    /* Lecture de la sortie */
    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);
    }

    /* suppression de la socket TCP */
    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 :
/* on vérifie les arguments */
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.

warning 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
/* Programme d'exemple ftpget :
 * permet la récupération d'un fichier dont l'URL est donnée en argument.
 */

#include "ftpget.h"

int main(int argc, char** argv)
{
    /* déclaration des variables */
    gchar* serveur = NULL;
    gchar* portStr = NULL;
    gint port = 80;
    gchar* filename = NULL, *outFilename = NULL;
    gchar* p = NULL;

    /* initialisation de GNet */
    gnet_init ();

    /* on vérifie les arguments */
    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 récupére les arguments */
    p = argv[1];

    /* extraction du préfixe */
    if (strncmp ("http://", p, sizeof ("http://") - 1) == 0)
    {
        p = &p[sizeof ("http://") - 1];
    }

    /* on récupére le nom du serveur */
    serveur = p;
    while (*p != 0 && *p != ':' && *p != '/') ++p;
        if ((p - serveur) == 0) { usage (); exit (EXIT_FAILURE); }
            serveur = g_strndup (serveur, p - serveur);

    /* récupération du port */
    if (*p == ':')
    {
        portStr = ++p;
        while (*p != 0 && *p != '/') ++p;
        if ((p - portStr) == 0) { usage (); exit (EXIT_FAILURE); }
            portStr = g_strndup (portStr, p - portStr);

        /* on convertit le port en int */
        port = atoi (portStr);
    }

    /* par défaut on récupére la racine du serveur */
    if (*p == 0)
        filename = g_strdup ("/");
    /* sinon on récupére le nom du fichier */
    else
        filename = g_strdup (p);

    /* on récupére le fichier */
    ftpget (serveur, port, filename, outFilename);

    /* libération de la mémoire */
    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

/* prototypes des fonctions */
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 :
/* on rappel l'utilisation de ftpget */
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* :
FILE* outFile;
On ouvre ensuite le fichier :
/* Ouverture du 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 :
/* fermeture du fichier */
fclose (outFile);
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"

/* on rappel l'utilisation de ftpget */
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)
{
    /* déclaration des variables */
    GInetAddr* iAddr;
    GTcpSocket* socket;
    GIOChannel* ioChannel;
    gchar* commande;
    gchar buffer[1024];
    GIOError error;
    guint n;
    FILE* outFile;

    /* Création de la inetAddr */
    iAddr = gnet_inetaddr_new (serveur, port);
    g_assert (iAddr != NULL);

    /* Création de la socket TCP */
    socket = gnet_tcp_socket_new (iAddr);
    gnet_inetaddr_delete (iAddr);
    g_assert (socket != NULL);

    /* Récupération du canal */
    ioChannel = gnet_tcp_socket_get_io_channel (socket);
    g_assert (ioChannel != NULL);

    /* Préparation et envoie de la commande */
    commande = g_strconcat ("GET ", filename, "\n", NULL);
    error = gnet_io_channel_writen (ioChannel, commande, strlen (commande), &n);
    g_free (commande);

    /* S'il y a une erreur */
    if (error != G_IO_ERROR_NONE)
    {
        g_warning ("Erreur: %d\n", error);
    }

    /* Ouverture du fichier */
    outFile = fopen (outFilename, "w");
    g_assert (outFile != NULL);

    /* Lecture de la sortie */
    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);
    }

    /* fermeture du fichier */
    fclose (outFile);

    /* suppression de la socket TCP */
    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

Pour ceux qui veulent aller plus loin, ils peuvent consulter le manuel de référence en français. fr le manuel de référence en français..


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+ :

gnet_nom_objet_action ()
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.

Vos questions techniques : forum d'entraide Accueil - Publiez vos articles, tutoriels, cours et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones. Nous contacter - Copyright 2000..2005 www.developpez.com