Sockets non connectées - Domaine Internet

© D. Mathieu    mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 01/10/1999 - Dernière mise à jour : 20/11/2001

Remarques préliminaires :

Sommaire

Nom de la machine hôte
hostname_t et Gethostname()
exo_01 : ppal()
Recherche d'une machine par son nom Internet
/etc/hosts
CExcFctNet
Classe CHostent
Gethostbyname()
exo_02 : ppal()
Recherche d'une machine par son adresse Internet
Gethostbyaddr()
CExcAddrIP
AddrIP() V.1
exo_03 : ppal()
Nouvelle version de Gethostbyname()
Découverte des services (numéros de port)
Recherche d'un service par son nom
/etc/services
Getservbyname()
CServent
Getservbyname()
exo_04 : ppal()
Recherche d'un service par son numéro de port
Getservbyport()
exo_05 : ppal()
Clients Internet de services universels

Client Internet non connecté d'echo

AddrIP()
Init_AddrIn()
ClientSockInDgram() et ClientConnectSockIn()
CstCodErrNet.h
exo_06c : ppal() du client
Client Internet non connecté de daytime
Sendto() et Recvfrom()
exo_07c : ppal() du client
Liste des machines, et des services disponibles sur la machine hôte
Serveur daytime UDP itératif
Surcharge d'Init_AddrIn()
Getsockname()
ServeurSockIn() et ServeurSockInDgram()
exo_09s : ppal() du serveur
exo_09c : ppal() du client
Serveur echo UDP itératif pseudo-parallèle
CExcPortIP
Getportbyname()
exo_10s : ppal() du serveur
exo_10c : ppal() du client
Serveur getpwnam UDP itératif
exo_11s : ppal() du serveur
exo_11c : ppal() du client


Nom de la machine hôte

exo_01

hostname_t et Gethostname()

     La fonction réseau gethostname() (fichier <unistd.h>), de profil suivant :
 
int gethostname (char *name, size_t namelen);

remplit la zone mémoire pointée par name avec le nom de la machine hôte. Le paramètre namelen indique la taille maximale de la zone pointée.

     Si le nom de la machine est plus long, le comportement de la fonction dépend largement de l'implémentation :

     Si le nom est plus court, un '\0' est ajouté en fin de nom. La taille maximale d'un nom de machine est donnée (en principe ...) par la constante MAXHOSTNAMELEN accessible par le fichier <sys/param.h> [1].

    Dans l'espace des noms nsNet, définir le type hostname_t qui doit être un vecteur de caractères de taille suffisante pour recevoir tout nom de machine, et le wrapper de profil suivant :
 
void Gethostname (hostname_t Nom) throw (CExcFctSyst);

exo_01 : ppal()

     Dans le fichier exo_01.cxx, écrire un programme qui affiche à l'écran le nom de la machine hôte.

Corrigés : nsNet.h    -    nsNet.hxx    -    exo_01.cxx

Sommaire


Recherche d'une machine par son nom Internet

exo_02

/etc/hosts

    Le fichier /etc/hosts contient un certain nombre d'informations correspondant à différentes machines (en fait différentes adresses Internet). Chaque ligne peut être considérée comme un élément d'un tableau - une entrée (entry) - correspondant à différentes représentations externes d'une adresse Internet, en principe dans l'ordre suivant :     Afficher le contenu du fichiers /etc/hosts et y jeter un coup d'oeil.

CExcFctNet

    Certaines fonctions de niveau 3 (fonctions non système) utilisées pour les applications réseau, renvoient 0 ou -1 en cas d'erreur et placent un code d'erreur dans une variable h_errno déclarée ainsi (fichier <netdb.h>) :
 
extern int h_errno;

    Dans les fichiers CExcFctNet.h et CExcFctNet.hxx du répertoire tpnet/include, écrire la classe CExcFctNet de l'espace de noms nsNet, dérivée de CExcFct, dont le constructeur utilise h_errno comme code d'erreur. Modifier le fichier INCLUDE_H en conséquence.

Classe CHostent

    La fonction C gethostbyname() renvoie un pointeur vers une structure qui contient des informations concernant la machine dont le nom lui est passé en paramètre. Cette structure hostent est déclarée ainsi dans le fichier  /usr/include/netdb.h :
 
struct  hostent
{
    char    *h_name;        /* official name of host */
    char    **h_aliases;    /* alias list */
    int     h_addrtype;     /* host address type */
    int     h_length;       /* length of address */
    char    **h_addr_list;  /* list of addresses from name server */
    #define h_addr  h_addr_list[0]  /* address, for backward compatiblity */
};

     Attention : lorsque le nom de la machine passé en paramètre est sous la forme nnn.nnn.nnn.nnn, la fonction gethostbyname() se contente (dans cette implémentation) :

     La classe CHostent, de l'espace de noms nsNet, est mise à votre disposition. Elle est dérivée de la struct hostent et offre les fonctionnalités suivantes :
 
int          GetNbreAliases (void)        const throw (); // renvoie le nombre d'aliases
int          GetNbreAddr    (void)        const throw (); // renvoie le nombre d'adresses Internet
                                                          // de la liste des adresses
const char * Get_h_name     (void)        const throw (); // renvoie le nom de la machine
const char * Get_h_aliases  (const int i) const throw (); // renvoie le ieme alias de la
                                                          // machine (pointeur nul si i est invalide)
in_addr      Get_h_addr     (const int i) const throw (); // renvoie l'adresse Internet de rang i
                                                          // de la liste des adresses renvoie 
                                                          // l'adresse INADDR_NONE si l'indice est invalide)

    Récupérer les fichiers CHostent.h, CHostent.hxx et CHostent.cxx dans les répertoires appropriés (tpnet/include et tpnet/util : voir Chemins d'accès aux sources des corrigés). Les placer dans vos répertoires correspondants (include et util) et faire les modifications nécessaires des fichiers INCLUDE_H et MakeUtil.

    Seuls les corps des deux fonctions GetNbreAliases() et GetNbreAdresses() sont placés dans le fichier CHostent.cxx. Le type in_addr est déclaré dans le fichier <netinet/in.h>.

    Passer un peu de temps à analyser cette classe.

Gethostbyname()

    Dans les fichiers nsNet.h et nsNet.cxx [2], ajouter le wrapper Gethostbyname() de profil :
 
CHostent * Gethostbyname (const char * Nom) throw (CExcFctNet) [3];

qui renvoie un pointeur vers un objet de la classe CHostent décrite ci-dessus. La fonction C (de niveau 3) gethostbyname() renvoie un pointeur nul en cas d'échec et positionne la variable globale h_errno. Parmi toutes les causes d'erreurs, la plus fréquente est la machine qui n'est pas trouvée. Cette cause d'erreur peut être identifiée si la variable h_errno a pour valeur HOST_NOT_FOUND.

    Afin de faciliter la tâche de l'utilisateur, ajouter dans l'exception levée par Gethostbyname() le nom de la machine en cause.

exo_02 : ppal()

    Dans le fichier exo_02.cxx, écrire un programme qui affiche les caractéristiques d'une machine dont le nom lui est passé en argument de la commande, à savoir :

    Compléter le fichier Makefile.

    Compiler et essayer sur la machine host locale (dont vous avez obtenu le nom dans l'exercice précédent), puis sur n'importe quelle machine que vous pouvez connaître (attention, si elle n'existe pas ou si elle est éloignée et peu fréquemment accédée, ça peut durer ...un certain temps!!). A défaut d'idée, chercher la machine www.altavista.digital.com puis altavista.com. Essayer aussi sur un nom de machine bidon.

Remarque : Dans cet exercice, vous venez de réaliser une partie de la commande nslookup(8), que vous pouvez essayer :
 
nslookup www.altavista.digital.com

Corrigés : exo_02.cxx    -    Makefile    -    CExcFctNet.h    -    CExcFctNet.hxx    -    INCLUDE_H    -    MakeUtil    -    nsNet.h    -    nsNet.cxx

Sommaire


Recherche d'une machine par son adresse Internet

exo_03

Gethostbyaddr()

    La fonction C (de niveau 3) gethostbyaddr(), comme son nom l'indique, renvoie un pointeur sur une struct hostent (vue plus haut) décrivant la machine dont l'adresse lui est passée en paramètre. Son profil est le suivant (fichier <netdb.h>) :
 
struct hostent *gethostbyaddr (const char *addr, int len, int type);

    Dans la version actuelle de cette fonction, le paramètre addr doit obligatoirement pointer sur une adresse IPv4 (dans l'ordre réseau [5] : malgré les apparences, addr ne pointe pas sur une chaîne de caractères, mais sur le premier octet d'un entier stocké sur 4 octets, un int sur cette machine).

    Le paramètre len est obligatoirement le nombre d'octets d'une adresse IPv4 (4 octets).

    Le type est obligatoirement une adresse Internet (= AF_INET). Il est donc inutile d'obliger l'utilisateur à utiliser ce profil compliqué.

   Dans les fichiers nsNet.h et nsNet.cxx, ajouter le wrapper Gethostbyaddr() de profil :
 
CHostent * Gethostbyaddr (const in_addr & Adresse) throw (CExcFctNet);

à laquelle on se contente de passer une adresse Internet de type in_addr (fichier <netinet/in.h>). Comme précédemment, la fonction doit lever une exception CExcFctNet dans tous les cas d'erreurs et ajouter dans le libellé la valeur de l'adresse IP en cause. Comme celle-ci est de type in_addr, donc pas éditable directement, il faut la transformer en une chaîne de caractères, en notation pointée, grâce à la fonction inet_ntoa() (fichier <arpa/inet.h>).

   L'adresse IP d'une machine est souvent connue en notation pointée : nnn.nnn.nnn.nnn. La fonction C inet_aton() (fichier <arpa/inet.h>), de profil :
 
int inet_aton (const char * cp, struct in_addr * inp);

convertit l'adresse IPv4 qui lui est passée sous forme d'une chaîne de caractères en notation pointée, par son paramètre cp, en une adresse in_addr (dans l'ordre réseau) qu'elle renvoie par le paramètre inp. Elle renvoie une valeur nulle si l'adresse est invalide.

CExcAddrIP

    Dans les fichiers CExcFctNet.h et CExcFctNet.hxx, ajouter la classe CExcAddrIP, dérivée de la classe CExcFct.

    Ajouter une donnée-membre m_Adresse de type string.

    Le constructeur de la classe CExcAddrIP doit avoir :

    On rappelle qu'un destructeur virtuel est nécessaire (car la classe dérive de façon lointaine de CEditable).

    Une nouvelle donnée-membre ayant été ajoutée, la fonction _Edit() doit être surchargée.

    Comme cela a été fait précédemment, nous centraliserons tous les codes d'erreurs nécessaires aux TPs de réseau dans un fichier unique : recopier le fichier tpsys/include/CstCodErr.h dans tpnet/include/CstCodErrNet.h. Supprimer tous les codes d'erreurs, et, toujours dans l'espace de noms std, ajouter les codes suivants :
 
CstAddrIPInval   = 201, // Adresse IP invalide
CstPortIPInval   = 202, // Port    IP invalide
CstPortIPNoExist = 203, // Service inexistant

qui nous serviront ultérieurement.

    Mettre à jour les macros CEXCFCTNET_H et CEXCFCTNET_HXX dans le fichier INCLUDE_H et ajouter la macro CSTCODERRNET_H.

AddrIP() V.1

    Dans les fichiers nsNet.h et nsNet.cxx, ajouter le wrapper AddrIP() de la fonction inet_aton(), de profil :
 
in_addr AddrIP (const char * Nom) throw (CExcAddrIP); [4]

qui renvoie l'adresse IP ou lève une exception CExcAddrIP de code CstAddrIPInval si la fonction C inet_aton() qu'il appelle renvoie 0.

    Dans le fichier MakeUtil, ajouter la nouvelle dépendance de nsNet.cxx à CstCodErrNet.h.

exo_03 : ppal()

    Recopier le fichier exo_02.cxx dans le fichier exo_03.cxx. Le programme doit maintenant afficher les caractéristiques d'une machine dont l'adresse IP lui est passée en argument de la commande sous forme de chaîne nnn.nnn.nnn.nnn.

    Compiler et tester. Essayer par exemple 127.0.0.1. Essayer aussi une adresse invalide (333.333.333.333 par exemple).

Corrigés : exo_03.cxx      -      nsNet.h      -      nsNet.hxx      -      nsNet.cxx      -      CExcFctNet.h      -      CExcFctNet.hxx      -      CstCodErrNet.h

Sommaire


Nouvelle version de Gethostbyname()

    Modifier le wrapper Gethostbyname() de façon que son paramètre Nom puisse aussi être considéré comme une adresse IPv4 sous forme pointée : nnn.nnn.nnn.nnn (on ignorera pour le moment les adresses IPv6). Pour cela, il suffit de tester le premier caractère, numérique ou non, au moyen de la fonction C isdigit() (fichier inclus <cctype>), pour choisir entre les deux représentations. Si c'est une adresse en notation pointée, il suffit d'appeler la fonction Gethostbyaddr(), en convertissant la chaîne par AddrIP(). De ce fait, la fonction Gethostbyname() est aussi susceptible de lever une exception CExcAddrIP, qui doit être ajoutée à son profil.

    Refaire l'édition de lien du fichier exo_02.o et tester. La commande nslookup(8) déjà citée plus haut effectue le même traitement.

Corrigés : nsNet.h      -      nsNet.cxx

Sommaire


Découverte des services (numéros de port)

Recherche d'un service par son nom

exo_04

/etc/services

    Le fichier /etc/services contient un certain nombre d'informations correspondant à différents services disponibles. En afficher le contenu et y jeter un coup d'oeil.

    Chaque ligne peut être considérée comme un élément d'un tableau - une entrée (entry) - contenant les informations suivantes :

    En voici un extrait :
 
# Description:  The services file lists the sockets and protocols used for
#               Internet services.
#
# Syntax:  ServiceName PortNumber/ProtocolName [alias_1,...,alias_n] [#comments]
#
# ServiceName           official Internet service name
# PortNumber            the socket port number used for the service
# ProtocolName          the transport protocol used for the service
# alias                 unofficial service names
# #comments             text following the comment character (#) is ignored

et, plus loin :
 
echo            7/tcp
echo            7/udp
discard         9/tcp           sink null
discard         9/udp           sink null
daytime         13/tcp
daytime         13/udp
netstat         15/tcp
quote           17/udp          text

CServent

    La fonction C getservbyname(), (fichier <netdb.h>) de profil suivant :
 
struct servent * getservbyname (const char * name, const char * proto);

renvoie un pointeur vers une structure contenant des informations concernant le service dont le nom lui est passé en argument. Ces informations sont celles indiquées dans le fichier /etc/services. Elle renvoie un pointeur nul si le service n'est pas trouvé.

    La structure servent est déclarée ainsi dans le fichier /usr/include/netdb.h :
 
struct servent
{
    char    *s_name;     /* Nom officiel du service */
    char    **s_aliases; /* Liste des aliases */
    int     s_port;      /* Numéro de port dans l'ordre réseau */
    char    *s_proto;    /* Protocole à utiliser */
};

     La classe CServent, de l'espace de noms nsNet, est mise à votre disposition. Elle est dérivée de la struct servent et offre les fonctionnalités suivantes :
 
int            GetNbreAliases (void)        const throw (); // renvoie le nombre d'aliases
const char *   Get_s_name     (void)        const throw (); // renvoie le nom officiel du service
const char *   Get_s_aliases  (const int i) const throw (); // renvoie le ieme alias du
                                                            // service (pointeur nul si
                                                            // i est invalide)
unsigned short Get_s_port     (void)        const throw (); // renvoie le numero de port du
                                                            // service (ordre reseau)
const char *   Get_s_proto    (void)        const throw (); // renvoie le protocole utilise

    Récupérer les fichiers CServent.h, CServent.hxx et CServent.cxx dans les répertoires appropriés (include et util : voir Chemins d'accès aux sources des corrigés). Les placer dans vos répertoires correspondants (include et util) et faire les modifications nécessaires des fichiers INCLUDE_H et MakeUtil.

Getservbyname()

    Dans l'espace de noms nsNet (fichiers nsNet.h et nsNet.hxx), ajouter le wrapper Getservbyname() de profil :
 
CServent * Getservbyname (const char * Nom, const char * Protocole) throw ();

qui appelle la fonction getservbyname() et renvoie un pointeur vers un objet de la classe CServent ou un pointeur nul si le service n'est pas trouvé.

exo_04 : ppal()

    Recopier le fichier exo_02.cxx dans le fichier exo_04.cxx. Le modifier pour qu'il affiche toutes les caractéristiques du service dont le nom et le protocole utilisé sont passés en arguments de la commande.

    Modifier le fichiers Makefile : exo_04.cxx dépend de $(CSERVENT_H).

    Tester sur des services existant ou pas (à comparer avec le contenu de /etc/services).

Corrigés : exo_04.cxx    -    Makefile    -    MakeUtil    -    INCLUDE_H    -    nsNet.h    -    nsNet.hxx

Sommaire


Recherche d'un service par son numéro de port

exo_05

Getservbyport()

    A l'espace de noms nsNet (fichiers nsNet.h et nsNet.hxx), ajouter le wrapper de prototype :
 
CServent * Getservbyport (unsigned short Port, const char * Protocole) throw ();

qui appelle la fonction C getservbyport(), et renvoie un pointeur sur un objet de la classe CServent contenant les caractéristiques du service dont le numéro de port (en ordre réseau) et le protocole lui sont passés en paramètres. Ces caractéristiques sont celles indiquées dans le fichier /etc/services. Le wrapper renvoie un pointeur nul si le service n'est pas trouvé.

exo_05 : ppal()

    Recopier le fichier exo_04.cxx dans le fichier exo_05.cxx. Le modifier pour qu'il admette comme premier paramètre soit le nom d'un service, soit son numéro de port dans l'ordre host (on se fondera sur le premier caractère, numérique ou non).

    Compiler et tester.

Corrigés : exo_05.cxx    -    nsNet.h    -    nsNet.hxx

Sommaire


Clients Internet de services universels

Client Internet non connecté d'echo

exo_06

AddrIP()

    La fonction AddrIP() écrite précédemment dans l'exo_03 doit être complétée pour permettre de trouver l'adresse IP d'une machine à partir de son nom, et plus seulement de son adresse en notation pointée. Pour cela, elle doit tester si le premier caractère de l'adresse est alphabétique. Si oui, elle appelle la fonction déjà écrite Gethostbyname() (attention : récursivité croisée) qui lui renvoie un pointeur de CHostent dont elle extrait l'adresse IPv4 grâce à la fonction membre Get_h_addr(0). La fonction AddrIP() est maintenant susceptible de lever aussi une exception CExcFctNet.

Init_AddrIn()

    Sur le même modèle que la fonction Init_AddrUn(), écrire dans les fichiers nsNet.h et nsNet.cxx la fonction Init_AddrIn(), de profil :
 
void Init_AddrIn (::sockaddr_in & Addr, unsigned short Port,
                  const ::in_addr & Adresse) throw ();

ClientSockInDgram() et ClientConnectSockIn()

    A l'espace de noms nsNet (fichiers nsNet.h et nsNet.cxx), ajouter les fonctions ClientSockInDgram() et ClientConnectSockIn() de profils :
 
int  ClientSockInDgram   (void) throw (CExcFctSyst);
void ClientConnectSockIn (int sd, const ::in_addr & Adresse,
                          unsigned short int Port)
                          throw (CExcFctSystFile);

    La fonction ClientSockInDgram() est semblable à la fonction ClientSockUnDgram(), étudiée dans le TP sur les sockets Unix. Cependant, l'appel de la fonction bind() n'est pas nécessaire pour que le serveur reçoive l'adresse de l'expéditeur.

    La fonction ClientConnectSockIn() effectue une pseudo-connexion entre un socket descriptor et un service désigné par une adresse IP et un numéro de port donné dans l'ordre réseau.

    La fonction connect() appelée à l'intérieur doit recevoir une adresse de type sockaddr_in, définie par :
 
struct sockaddr_in
{
    sa_family_t        sin_family; // doit être AF_INET
    unsigned short int sin_port;
    struct in_addr     sin_addr;
    unsigned char      sin_zero[8];
};

CstCodErrNet.h

    Dans le fichier CstCodErrNet.h, ajouter la constante
 
CstTimeOut       = 204, // Délai expiré

exo_06c : ppal() du client

    Ecrire dans le fichier exo_06c.cxx un programme qui envoie une chaîne de caractères au service echo d'une machine dont le nom est passé en argument avec le protocole udp. Utiliser la fonction AddrIP(). Le service lui renvoie la même chaîne, que le programme affiche. Faire une pseudo-connexion puis utiliser par exemple les wrappers Read() et Write().

    Essayer plusieurs machines (alpha, romarin, www.altavista.digital.com, etc.., par exemple www.microsoft.com pas toujours très rapide !.).

     Après cette tentative peut-être infructueuse, il est temps de doter le client d'un timer non cyclique : ajouter une alarme (fonction alarm()) permettant d'abréger l'attente au bout d'un temps qui sera passé en argument de la commande. Si la lecture s'effectue normalement avant épuisement du délai, il importe de désarmer le timer le plus rapidement possible afin de ne pas être interrompu ultérieurement (::alarm (0)). La réception du signal correspondant doit terminer le programme après affichage d'un message. La constante CstTimeOut peut alors être renvoyée à la fonction main().

     Compiler et retester sur les mêmes machines.

Corrigés : CstCodErrNet.h    -    nsNet.h    -    nsNet.cxx    -    exo_06c.cxx

Sommaire


Client Internet non connecté de daytime

exo_07

Sendto() et Recvfrom()

     Ajouter à nsNet (.h et .hxx) les wrappers des fonctions sendto() et recvfrom() de profils suivants :
 
size_t Recvfrom (int socket, void * buffer,
                 size_t length, int flags = 0)
                                  throw (CExcFctSystFile);

size_t Recvfrom (int socket, void * buffer,
                 size_t length, int flags,
                 sockaddr_in & address)
                                  throw (CExcFctSystFile);

size_t Sendto (int socket, const void * buffer,
                 size_t length, int flags,
                 sockaddr_in & address)
                                  throw (CExcFctSystFile);

     La première fonction peut être utilisée avec n'importe quel type de socket (par exemple Unix ou Internet) : le processus qui reçoit le datagramme ne cherche pas à connaître l'expéditeur.

     Les deux autres fonctions, acceptant une adresse de type sockaddr_in, sont destinées à des applications non connectées communiquant par Internet exclusivement.

exo_07c : ppal() du client

     Ecrire dans le fichier exo_07c.cxx un programme qui demande la date au service daytime d'une machine dont le nom est passé en paramètre, avec le protocole udp. Utiliser les fonctions AddrIP(), Recvfrom() et Sendto(). Le service lui renvoie une chaîne de longueur variable selon les systèmes, que le programme affiche (voir TP "Sockets Unix" : serveur daytime en mode connecté et client correspondant).

     Pour interroger le serveur, on peut en principe lui envoyer n'importe quelle donnée, y compris de longueur nulle. Le protocole IP envoie alors une trame vide (ne contenant que les entêtes UDP et IP), et la fonction recvfrom() à l'autre extrémité renvoie 0 (ce qui ne signifie pas une "fin-de-fichier", sans signification en mode non connecté !). Il semble cependant que certaines implémentations n'envoient rien si la longueur est nulle, et il est donc prudent d'envoyer un caractère au moins.

     On peut garder le principe de l'alarme introduite dans l'exercice précédent.

     Essayer plusieurs machines dont celle sur laquelle vous travaillez.

Corrigés : nsNet.h    -    nsNet.hxx    -    exo_07c.cxx

Sommaire


Liste des machines, et des services disponibles sur la machine hôte

exo_08
    Le fichier de la base de données réseau de la machine hôte (/etc/hosts dans notre cas) est automatiquement ouvert, si nécessaire, par l'appel aux fonctions gethostbyname() et gethostbyaddr() et reste ouvert après cet appel jusqu'à la fin du programme (sauf action spécifique contraire). Il est possible de le refermer explicitement par l'appel sethostent(0), qui reste sans action si le fichier est fermé. L'appel gethostent() renvoie :     Pour être sûr d'explorer la totalité de la base de données, il est donc nécessaire de se placer en début du fichier en début de programme. Il suffit donc de commencer l'application en fermant le fichier. L'appel endhostent() referme lui aussi le fichier /etc/hosts s'il a été ouvert préalablement par appel à l'une des fonctions gethostbyname() ou gethostbyaddr(), sauf si entre-temps la fonction sethostent() a été appelée avec un paramètre effectif non nul.

     Les profils des fonctions (pas tous dans le man) sont les suivants :

void             sethostent (int stayopen);
struct hostent * gethostent (void);
void             endhostent (void);

    Dans le fichier exo_08.cxx, écrire un programme qui affiche les noms de toutes les machines répertoriées dans le fichier local de la base de données réseau de la machine hôte, /etc/hosts. Utiliser pour cela les fonctions sethostent(), gethostent() et endhostent() décrites ci-dessus.

    Compiler et tester.

    Symétriquement existent les trois fonctions setservent(), getservent() et endservent() qui explorent le fichier /etc/services. Compléter le fichier exo_08.cxx pour qu'il affiche aussi la liste des services disponibles.

Corrigés : exo_08.cxx

Sommaire


Serveur daytime UDP itératif

exo_09

Surcharge d'Init_AddrIn()

    La structure d'un serveur utilisant une socket non connectée dans le domaine Internet est tout-à-fait semblable à celle évoquée pour le mode non connecté dans le domaine Unix : obtention d'un socket descriptor (socket()) suivie d'un lien (bind()) avec une socket désignée par son adresse de type sockaddr_in, définie plus haut (sauf l'appel à la fonction unlink(), spécifique des sockets Unix).

    Le champ sin_port doit être le numéro de port (numéro de service) que le serveur met à disposition. Il doit être exprimé en ordre "réseau". S'il s'agit d'un service nouvellement créé (non encore répertorié par l'administrateur-système dans le fichier /etc/services), il est conseillé de ne pas donner un numéro de port particulier, mais de le laisser choisir par le système. Ainsi, on peut être assuré que le service n'existe pas au moment où le serveur demande sa création. Pour cela il faut mettre le champ sin_port à zéro. Après exécution de la fonction bind(), le numéro de port devra être récupéré (voir plus loin la fonction getsockname()).

    Le champ sin_addr doit contenir l'adresse IP de la machine qui héberge le service (donc le serveur, donc le processus en train d'être écrit). Cette adresse peut être obtenue par la fonction gethostname() par exemple, mais il est conseillé d'utiliser plutôt l'adresse "passe-partout" INADDR_ANY qui laisse au système le soin de remplir ce champ. Rappelons en effet qu'une même machine peut avoir plusieurs adresses IP et qu'il n'y a aucune raison qu'un serveur n'accepte de demandes de connexion que sur une seule de ses adresses IP.     La macro INADDR_ANY n'est pas de type in_addr, nécessaire pour être utilisée comme paramètre de Init_AddrIn(). Afin d'éviter à l'utilisateur de devoir systématiquement résoudre ce problème (un simple transtypage ne suffit pas ici), il est plus élégant de proposer une surcharge de la fonction admettant une adresse IP de type unsigned long :
 
void Init_AddrIn (::sockaddr_in & Addr,
                  unsigned short Port     = 0,
                  unsigned long AdresseIP = INADDR_ANY) throw ();

Getsockname()

    Ajouter à l'espace de noms nsNet (fichiers nsNet.h et nsNet.hxx) le wrapper de la fonction getsockname() correspondante, de profil :
 
void Getsockname (int sd, sockaddr_in & Addr) throw (CExcFctSystFile);

    Analogue au wrapper Getsockname(), écrit pour les sockets Unix, elle remplit une structure sockaddr_in à partir d'un descripteur sd de socket Internet, ce qui permet de connaître l'adresse IP et le numéro de port correspondant.

ServeurSockIn() et ServeurSockInDgram()

    Ajouter à l'espace de noms nsNet (fichiers nsNet.h et nsNet.cxx) les fonctions de profils :
 
int  ServeurSockIn      (unsigned short & NumPort, const int Type) throw (CExcFctSyst);
int  ServeurSockInDgram (unsigned short & NumPort)                 throw (CExcFctSyst);

    Comme précédemment la fonction ServeurSockUn(), la fonction ServeurSockIn() doit être privée (dans l'espace de noms anonyme du fichier nsNet.cxx). En résumé, elle appelle successivement les fonctions socket(), bind() puis getsockname() afin de récupérer le numéro de port.

    La fonction ServeurSockInDgram() se contente d'appeler la fonction ServeurSockIn() avec le type de socket SOCK_DGRAM.

exo_09s : ppal() du serveur

    Dans le fichier exo_09s.cxx, écrire le serveur UDP sur Internet qui renvoie la date à tout client qui interroge sa socket (faire une boucle infinie). Passer le numéro de port de la socket en paramètre de la commande (essayer le numéro qui vous a été attribué, puis essayer la valeur 0). Après création de la socket par appel de la fonction ServeurSockInDgram(), afficher le numéro de port renvoyé par la fonction.

exo_09c : ppal() du client

    Recopier le fichier exo_07c.cxx dans le fichier exo_09c.cxx pour qu'il puisse tester votre serveur : il doit lire le numéro de port en argument. Vérifier que la réponse qu'il reçoit est bien en provenance du serveur auquel il s'est adressé.

    Compiler et tester.

Corrigés : nsNet.h    -    nsNet.hxx    -    nsNet.cxx    -    exo_09s.cxx    -    exo_09c.cxx

Sommaire


Serveur echo UDP itératif pseudo-parallèle

exo_10
    Il s'agit ici de faire un serveur d'écho qui boucle indéfiniment en renvoyant à l'expéditeur le datagramme qu'il a reçu. Le pseudo-parallélisme sera illustré par le fait que plusieurs clients seront lancés simultanément.

CExcPortIP

    Tout d'abord ajouter l'exception CExcPortIP analogue à l'exception CExcAddrIP dans les fichiers adéquats. La donnée-membre m_Port stocke le numéro de port invalide sous forme de chaîne de caractères (string).

Getportbyname()

    Puis ajouter à l'espace des noms nsNet (fichiers nsNet.h et nsNet.cxx) les fonctions :
 
unsigned short Getportbyname (const char * Nom)       throw (CExcPortIP);
unsigned short Getportbyname (const char * Nom,
                              const char * Protocole) throw (CExcPortIP);

    Attention : ces fonctions ne sont pas des wrappers de fonctions systèmes, mais des utilitaires permettant de transformer un numéro de port passé sous forme d'une chaîne, ou un couple {nom de service, nom de protocole} en un numéro de port directement utilisable (en ordre réseau).

    La fonction Getportbyname() renvoie le numéro de port (en "ordre réseau")

exo_10s : ppal() du serveur

    Recopier le fichier exo_09s.cxx dans le fichier exo_10s.cxx. Modifier le serveur UDP pour qu'il renvoie à tout client le message qu'il en reçoit (faire une boucle infinie).

exo_10c : ppal() du client

    Recopier le fichier exo_09c.cxx dans le fichier exo_10c.cxx. Ajouter l'argument délai (en secondes) à la commande de lancement du client. Utiliser la fonction Getportbyname() ci-dessus pour convertir le numéro de port.

     Le client doit envoyer au serveur plusieurs fois (par exemple 5 fois) le même message par exemple "Emission toutes les xxx secondes", puis récupérer et afficher la réponse du serveur à l'écran. Tester avec plusieurs clients simultanés, de périodicités différentes.

Corrigés : nsNet.h    -    nsNet.cxx    -    CExcFctNet.h    -    CExcFctNet.hxx    -    exo_10s.cxx    -    exo_10c.cxx

Sommaire


Serveur getpwnam UDP itératif

exo_11

exo_11s : ppal() du serveur

    Ecrire dans le fichier exo_11s.cxx le serveur UDP qui renvoie à tout client qui le demande le répertoire principal et le shell correspondant à n'importe quel username. Pour obtenir ces informations, utiliser la fonction getpwnam() qui, lorsqu'on lui passe une chaîne de caractères contenant un username, renvoie un pointeur vers une structure passwd contenant de nombreux champs parmi lesquels ceux qui sont demandés (inclure le fichier pwd.h).

exo_11c : ppal() du client

    Ecrire dans le fichier exo_11c.cxx le client UDP qui envoie au serveur un username, et affiche le répertoire home et le shell qu'il reçoit en retour. Si le username n'existe pas, il reçoit seulement le message : "username inconnu".

Corrigés : exo_11s.cxx    -    exo_11c.cxx

Sommaire


[1] En réalité, définie dans le fichier <asm/param.h> dans la version actuelle de Linux RedHat 6, et vaut 64.

[2] La version actuelle de la fonction Gethostbyname() ne comporte qu'une ligne et mériterait d'être inline. Cependant, elle sera augmentée dans un exercice ultérieur, ce qui justifie sa présence dans le fichiers nsNet.cxx.

[3] Le profil actuel de la fonction Gethostbyname() sera complété dans l'exercice suivant.

[4] Le profil actuel de la fonction AddrIP() sera complété ultérieurement.

[5] Rappel : les transformations d'ordre "host" en ordre "net" et inverses sont effectuées au moyen des fonctions C ntohs(), htons(), ntohl(), htonl().


Téléchargement des corrigés :

tpsidgram.zip

Chemins d'accès aux sources des corrigés :

~mathieu/PARTAGE/src/tp/tpnet/tpsidgram/include
~mathieu/PARTAGE/src/tp/tpnet/tpsidgram/util
~mathieu/PARTAGE/src/tp/tpnet/tpsidgram/dirsidgram