JL A 2 01 5, A G 2 02 1
Les fonctions simplifiant
l'utilisation de l'interface socket d'accès aux
protocoles TCP/IP
JL A 2 01 5, A G 2 02 1
TCP/IP : historique Fonctions ou primitives
des protocoles
JL A 2 01 5, A G 2 02 1
La première réalisation a été intégrée dans la distribution 4.1 BSD en 1981
Les auteurs en étaient Bolt-Beranek et Newman
Composée de 10 fonctions ou primitives lorsque le protocole est intégré au système d'exploitation
Six fonctions permettant de créer, spécifier les caractéristiques et terminer la communication:
socket, bind,listen, accept, connect, shutdown Quatre fonctions permettant l'échange de données
send et receive permettant l'échange de données en mode connecté ( protocoleTCP)
sendto et recvfrom permettant l'échange de données
en mode non connecté (protocole UDP)
JL A 2 01 5, A G 2 02 1
En plus de ces primitives il est possible, en mode connecté, d'utiliser les primitives généralistes
d'Entrée/Sortie:
read
write
close
JL A 2 01 5, A G 2 02 1
L'utilisation des primitives et le travail réalisé ne sont pas les mêmes dans un client et dans un serveur
Un serveur accepte généralement toute connexion venant de la part d'un client, il ne connaît donc pas l'adresse du client
en mode connecté cela est inutile
en mode non connecté les primitives doivent non seulement fournir les données reçues mais
également l'origine de ces données
Pour se connecter ou en envoyer des données en mode non connecté le client doit connaître l'adresse de la
machine du serveur et le port qu'il utilise
JL A 2 01 5, A G 2 02 1
Dans le cas d'un service général , ftp, rsh, ssh, ... le numéro de port est déterminé par le service utilisé et enregistré dans le fichier /etc/services
Il reste possible d'utiliser un serveur fournissant un service standard sur un port non standard en précisant le port utilisé par le serveur
L'interface a été conçue dans un cadre général, elle
n'est pas spécifique d'un ou d'une famille de protocoles Les familles accessibles sont:
domaine INET: TCP/IP: IP,TCP, UDP, ...
domaine XNS: SPP, XNS
domaine UNIX: FAUX RESEAU pas de protocole
“dédié” (X11)
JL A 2 01 5, A G 2 02 1
Types de serveurs/Protocoles
Serveur Itératif Serveur Concurrent
Mode connecté Rare
Connexion journalière Typique ftp Typique
Mode non
Connecté Rare
tftp
JL A 2 01 5, A G 2 02 1
Mode Connecté
JL A 2 01 5, A G 2 02 1
Itératif
Client socket()
Serveur bind()
listen()
s1<- accept()
recv/read() send/write() ...
shutdown()
socket() connect()
send/write recv/read()
shutdown() Sommeil
Réveil
Fin
JL A 2 01 5, A G 2 02 1
Remarques
La primitive accept étant bloquante le serveur : Attends la connexion d'un client
Traite les demandes du client jusqu'à ce qu'il se déconnecte
Attends le connexion d'un nouveau client
Le traitement des clients est donc séquentiel
A tout instant le serveur ne traite les requêtes que
d'un seul client
JL A 2 01 5, A G 2 02 1
Parallèle
Client s<-socket()
Serveur
bind() listen() s1<-accept()
...
close(s)
socket() connect()
send/write recv/read()
shutdown() Réveil
Fin
fork()
shutdown(s1) close(s)
close(s1)
recv/read() Sommeil
send/write()
JL A 2 01 5
Remarques
Le traitement des clients est parallèle et effectué dans un processus fils
Il existe autant de processus fils que de clients connectés
Le serveur père ne fait qu'attendre la connexion d'un client dans une boucle infinie
Dès qu'un client est connecté un processus fils est créé et traite les requêtes
Le processus père se met en attente d'une
nouvelle connexion
JL A 2 01 5, A G 2 02 1
Non connecté
JL A 2 01 5, A G 2 02 1
Client socket()
Serveur bind() recv()
sendto() ...
close()
socket()
sendto() recvfrom()
shutdown() Réveil
Sommeil
bind()
Fin
Traitement
Itératif
JL A 2 01 5
Remarques
La primitive recv est bloquante mais permet de recevoir des données sans désignation de
l'émetteur
Contrairement au cas connecté les différentes requêtes clientes sont toutes reçues sur la même socket
Le fonctionnement est « parallèle » au sens ou il n'y a pas de début et fin de session
Pour traiter les requêtes d'un client particulier on
utilise recvfrom
JL A 2 01 5, A G 2 02 1
Client s<-socket()
Serveur
bind()
...
close(s)
socket() connect()
sendto() recvfrom()
shutdown() Réveil
recv() fork()
sendto() close(s) Fin
sendto()
recvfrom()
Parallèle
JL A 2 01 5, A G 2 02 1
Structures utilisées
famille no de port
14 octets dépendant de la famille adresse de
la machine
famille
inutilisé
0 16 0 16
sockaddr_in sockaddr
JL A 2 01 5, A G 2 02 1
struct sockaddr_in { short sin_family;
u_short sin_port;
struct in_addr;
char sin_zero[8];
}
struct in_addr{
unsigned long int s_addr
}
struct sockaddr {
u_short sa_family;
char sa_data[14];
}
famille de la socket
adresse non pointée numéro de port
non décrit
ATTENTION: TOUTES LES INFORMATIONS DOIVENT ETRE
AU FORMAT RESEAU
JL A 2 01 5, A G 2 02 1
Association et primitives
Association = {Protocole,@émetteur,port émetteur, @destinataire,port_destinataire}
Serveur socket() bind() listen(), accept()
Client socket() connect()
Serveur socket() bind() recvfrom()
Client socket() bind() sendto()
Mode
connecté Protocole Adresse locale,
numéro de port. Adresse distante,
numéro de port.
Mode
non connecté Protocole Adresse locale,
numéro de port. Adresse distante,
numéro de port.
JL A 2 01 5, A G 2 02 1
FONCTIONS DE CRÉATION DE
CLIENT/SERVEUR
JL A 2 01 5, A G 2 02 1
Mode connecté : TCP
JL A 2 01 5, A G 2 02 1
Serveur TCP
Le serveur doit créer une socket et réserver un port afin que les clients puissent se connecter. Ceci est réalisé par la fonction:
int creer_serveur_tcp ( int port, int max_client, int debug)
Les paramètres sont :
port : le numéro de port que souhaite utiliser le serveur
max_client : le nombre maximal de clients dont le serveur accepte la connexion
debug : si différent de zéro permet d'obtenir
une trace du déroulement de la fonction
JL A 2 01 5, A G 2 02 1
>= 0 : la création du serveur est réussie, le
résultat est le descripteur de la socket créée qui doit être utilisé dans la suite des opérations
< 0 : il s'est produit une erreur, la socket n'a pu être créée, le port réservé ... Le résultat
retourné est celui retourné par la dernière
primitive utilisée, celle qui a échoué. Dans ce cas le programme appelant ne peut se
poursuivre, l'utilisation du troisième paramètre avec une valeur <> 0 permettra d'obtenir des informations sur la cause du problème.
Résultat retourné
JL A 2 01 5, A G 2 02 1
Attente de la connexion d'un client
JL A 2 01 5, A G 2 02 1
L'attente des connexions des clients est faite dans la fonction :
int attendre_client_tcp ( int socket_serveur, int debug)
●
Les paramètres sont :
socket : le résultat >0 retourné par la fonction creer_serveur_tcp
debug : idem fonction précédente.
●
Cette fonction met en attente le serveur jusqu'à la connexion d'un client.
●
Le résultat retourné, si > 0, est le descripteur de la socket
permettant de dialoguer avec le client, il est différent de celui passé comme premier paramètre
●
Un résultat < 0 indique une erreur.
JL A 2 01 5, A G 2 02 1
Client TCP
Le client doit fournir l'adresse IP de la machine et le numéro de port auquel il souhaite se connecter. La fonction :
int creer_client_tcp ( char * adresse, int port, int debug)
dont les paramètres sont :
adresse : adresse IP du serveur en notation pointée port : le numéro de port utilisé par le serveur
debug : idem fonctions précédentes
retourne le descripteur de la socket connectée au
serveur si >0. Si <0 une erreur a été rencontrée.
JL A 2 01 5, A G 2 02 1
EXEMPLES
JL A 2 01 5, A G 2 02 1
Client
JL A 2 01 5, A G 2 02 1
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "client_serveur.h"
#define TRACE 0
int main ( int argc, char ** argv ){
int socket, retour;
char * message = "Bonjour serveur"; /* 16 car avec \0 */
char message_serveur[256];
socket = creer_client_tcp ( "127.0.0.1", 6666 , TRACE);
if ( socket < 0 ) {
fprintf(stderr, "Echec de la connexion au serveur\n");
(void)close(socket);
return EXIT_FAILURE;
}
retour = write(socket,message, 16 );
if ( retour != 16 ) {
fprintf(stderr, "Echec de l\'envoi du message au serveur\n");
return EXIT_FAILURE;
}
JL A 2 01 5, A G 2 02 1
retour = read(socket,message_serveur,15);
if ( retour != 15 ) {
fprintf(stderr, "Echec de la reception du message du serveur : %d\n", retour);
return EXIT_FAILURE;
}
fprintf(stderr,"Message du serveur : %s\n", message_serveur);
(void)close(socket);
return EXIT_SUCCESS;
}
JL A 2 01 5, A G 2 02 1
Serveur se terminant après
la réception/émission
d'une chaîne de caractère
JL A 2 01 5, A G 2 02 1
Le serveur attend la connexion d'un client.
Après connexion il lit un message envoyé par le client.
Il émet à son tour un message au client et termine la connexion
Le client se termine après affichage sur la sortie
standard du message envoyé par le serveur.
JL A 2 01 5, A G 2 02 1
#define TRACE 0
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "client_serveur.h"
int main ( int argc, char ** argv ) {
int socket_serveur, socket_client, retour;
char message[256];
char * salut = "Bonjour client";
socket_serveur = creer_serveur_tcp ( 6666 , 4 , TRACE);
if ( socket_serveur < 0 ) {
fprintf(stderr,"Echec de la creation du serveur\n");
return EXIT_FAILURE;
}
socket_client = attendre_client_tcp ( socket_serveur, TRACE);
if ( socket_client < 0 ) {
fprintf(stderr, "Echec de l\'attente du client\n");
( void )close( socket_serveur);
return EXIT_FAILURE;
}
JL A 2 01 5, A G 2 02 1
retour = read(socket_client, message, 16);
if ( retour != 16 ) {
fprintf(stderr,"Le serveur n\a pas recu le message de 16 car\n");
(void) close( socket_client);
return EXIT_FAILURE;
}
fprintf(stdout, "Message recu : %s\n", message);
retour = write( socket_client, salut, 15);
if ( retour != 15 ) {
fprintf(stderr, "Echec de l\'evoi du message au client\n");
(void) close( socket_client);
return EXIT_FAILURE;
}
fprintf(stderr, "Serveur : fermeture de la socket client\n");
(void) close( socket_client);
return EXIT_SUCCESS;
}
JL A 2 01 5, A G 2 02 1
Serveur
Multiple clients traités en séquentiel
JL A 2 01 5, A G 2 02 1
#define TRACE 0
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "client_serveur.h"
int main ( int argc, char ** argv ) {
int socket_serveur, socket_client, retour;
char message[256];
char * salut = "Bonjour client";
socket_serveur = creer_serveur_tcp ( 6666 , 4 , TRACE);
if ( socket_serveur < 0 ) {
fprintf(stderr,"Echec de la creation du serveur\n");
return EXIT_FAILURE;
}
JL A 2 01 5, A G 2 02 1
while (1) {
socket_client = attendre_client_tcp ( socket_serveur, TRACE);
if ( socket_client < 0 ) {
fprintf(stderr, "Echec de l\'attente du client\n");
( void )close( socket_serveur);
return EXIT_FAILURE;
}
retour = read(socket_client, message, 16);
if ( retour != 16 ) {
fprintf(stderr,"Le serveur n\a pas recu le message de 16 car\n");
(void) close( socket_client);
return EXIT_FAILURE;
}
fprintf(stdout, "Message recu : %s\n", message);
retour = write( socket_client, salut, 15);
if ( retour != 15 ) {
fprintf(stderr, "Echec de l\'evoi du message au client\n");
(void) close( socket_client);
return EXIT_FAILURE;
}
fprintf(stderr, "Serveur : fermeture de la socket client\n");
(void) close( socket_client);
}
close(socket);
return EXIT_SUCCESS;
}
JL A 2 01 5, A G 2 02 1
Serveur
Multiple clients traités en parallèle
JL A 2 01 5, A G 2 02 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
int main ( int argc, char ** argv ) {
int socket_client, socket_serveur, retour, lr, le;
ssize_t envoye, recu;
char message[256];
int fils;
//création de la socket
socket_serveur = creer_serveur_tcp ( 6666 , 1 , 1);
if ( socket_serveur < 0 ) {
fprintf(stderr,"Echec de la creation du serveur\n");
exit(-1);
}
JL A 2 01 5, A G 2 02 1
while (1) {
//Attente d'un client
socket_client = attendre_client_tcp ( socket_serveur, 1);
if ( socket_client < 0 ) {
fprintf(stderr, "Echec de l\'attente du client\n");
( void )close( socket_serveur);
exit(-1);
}
//Création d'un processus fils pour dialoguer avec le client fils = fork();
switch (fils) { case -1 :
//echec de la création du processus fils fprintf(stderr,\
"Echec de la creation du processus fils\n");
(void) close (socket_serveur);
exit(-1);
break;
case 0 :
//Processus fils qui dialogue avec le client (void) close( socket_serveur);
//Début des échanges avec le client ...
//Fin des échanges avec le client (void) close( socket_client);
exit (0);
break;
} /* fin switch */
JL A 2 01 5, A G 2 02 1
//Le processus père n'utilise pas la socket //permettant de communiquer avec le client (void) close( socket_client);
//Continuation avec l'attente de la connexion d'un //nouveau client boucle while
} /* while */
(void) close( socket_serveur);
exit(0);
}
JL A 2 01 5, A G 2 02 1
Serveur
Multiple clients traités en parallèle et
utilisation d'un signal
pour arrêter le serveur
JL A 2 01 5, A G 2 02 1
Fonction signal : associer un traitement
à un signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal
(int signum, sighandler_t handler);
JL A 2 01 5, A G 2 02 1
Envoi d'un signal
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
Envoi du signal sig au processus pid
JL A 2 01 5, A G 2 02 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
//Déclarations globales à cause de la fonction fin_serveur pid_t fils, socket_serveur;
//Les fonctions de traitement des signaux :
/* SIGCHILD : réceptionné chaque fois qu'un fils se termine le père purge alors le code de retour avec
la primitive wait
SIGINT : réceptionné quand ^C est pressé au clavier il faut alors fermer les descripteur de sockets encore ouverts.
*/
JL A 2 01 5, A G 2 02 1
// Traitement de SIGCHILD void fin_fils (int n_signal){
if ( n_signal != SIGCHLD ) fprintf(stderr,\
"Serveur: etonnant la fonction de traitement de SIGCHILD est appelee avec un signal different de SIGCHILD\n");
(void) wait((void*) NULL);
fprintf(stderr,"Serveur: code de retour du fils purge\n");
(void) signal( SIGCHLD, fin_fils);
return ; }
// Traitement de SIGINT
void fin_serveur (int n_signal){
if ( n_signal != SIGINT ) fprintf(stderr,\
"Serveur: etonnant la fonction de traitement de SIGINT est appelee avec un signal different de SIGINT\n");
// envoi d'un signal de fin au fils éventuel (void) kill(fils, SIGTERM);
(void) wait((void*) NULL);
(void) close( socket_serveur);
exit(0);
}
JL A 2 01 5, A G 2 02 1
//int main ( int argc, char ** argv ) { int socket_client, retour, lr, le;
ssize_t envoye, recu;
char message[256];
/* Capture du signal de fin de fils */
(void) signal( SIGCHLD, fin_fils);
(void) signal( SIGINT , fin_serveur);
//création de la socket
socket_serveur = creer_serveur_tcp ( 6666 , 1 , 1);
if ( socket_serveur < 0 ) {
fprintf(stderr,"Echec de la creation du serveur\n");
exit(-1);
}
while (1) {
//Attente d'un client
socket_client = attendre_client_tcp ( socket_serveur, 1);
if ( socket_client < 0 ) {
fprintf(stderr, "Echec de l\'attente du client\n");
( void )close( socket_serveur);
exit(-1);
}
//Création d'un processus fils pour dialoguer avec le client fils = fork();
JL A 2 01 5, A G 2 02 1
switch (fils) { case -1 :
//echec de la création du processus fils fprintf(stderr,\
"Echec de la creation du processus fils\n");
(void) close (socket_serveur);
exit(-1);
break;
case 0 :
//Processus fils qui dialogue avec le client (void) close( socket_serveur);
//Début des échanges avec le client ...
//Fin des échanges avec le client (void) close( socket_client);
exit (0);
break;
}
//Le processus père n'utilise pas la socket //permettant de communiquer avec le client (void) close( socket_client);
//Continuation avec l'attente de la connexion d'un //nouveau client boucle while
}
(void) close( socket_serveur);
exit(0);
}
JL A 2 01 5, A G 2 02 1
UDP
JL A 2 01 5, A G 2 02 1
Création d'un serveur UDP
JL A 2 01 5, A G 2 02 1
Lors de la création le serveur doit préciser le numéro de port qu'il souhaite utiliser. Ce numéro de port sera utilisé par les clients pour lui envoyer des données
En mode non connecté il n'y a pas de circuit virtuel établi entre le serveur et le client.
Lors de la réception de données le serveur reçoit des informations sur l'émetteur par l'intermédiaire d'un paramètre donnée/résultat : une structure de type sockaddr
On passera donc un pointeur sur une structure de type
sockaddr en paramètre à la fonction recvfrom
JL A 2 01 5, A G 2 02 1
La création d'un serveur est réalisée par un appel à la fonction :
int creer_serveur_udp ( int port, int debug) dont les paramètres port et debug sont
identiques à ceux de la fonction creer_serveur_tcp
Cette fonction créé un serveur utilisant le port UDP passé en paramètre
Au retour une valeur négative indique un problème dans la création du serveur, une valeur supérieure à 0 le
descripteur de la socket créée.
Ce descripteur doit être utilisé pour émettre/recevoir des données.
debug permet d'obtenir une trace d'éxécution
JL A 2 01 5, A G 2 02 1
Création d'un client UDP
JL A 2 01 5, A G 2 02 1
La fonction :
int creer_client_udp ( char * adresse, int port, struct sockaddr_in * pserver, * pclient,
int debug )
créé un client utilisant le protocole UDP adresse et port sont ceux du serveur
pserver et pclient sont des pointeurs sur des structures de type sockaddr, ces structures doivent avoir été allouées par le programme appelant, elles seront utilisées dans la fonction
Au retour un résultat positif indique un succès et est le descripteur de la socket a utiliser par la suite. Une valeur
< 0 indique une erreur
JL A 2 01 5, A G 2 02 1
Exemple de serveur
JL A 2 01 5, A G 2 02 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#define TRACE 0
int main( int argc, char **argv ) { int sockfd,n ;
/* sockaddr_in structure pour le client */
struct sockaddr_in client;
int lg_client=sizeof( client );
char message[256]; /* le message du client */
char * bonjour="Bonjour client";
sockfd = creer_serveur_udp ( 9999, TRACE );
if ( sockfd < 0 ) {
fprintf(stderr,"Serveur: echec de la fonction creer_serveur_udp\n");
exit(-1);
}
JL A 2 01 5, A G 2 02 1
/* Boucle infinie */
while ( 1) { /*
Lecture de donnees envoyees par le processus client */
//fprintf(stderr,"Serveur: attente de donnees d\'un client\n");
n = recvfrom ( sockfd, message, 256 , 0 , (struct sockaddr *)&client , &lg_client);
if ( n <= 0 ) {
fprintf( stderr, "Pas de caracteres recus du client");
(void) close ( sockfd);
exit (-1);
}
printf("Message recu du client:%s\n",message);
/* Envoi du message au client */
n = sendto ( sockfd, bonjour , strlen(bonjour)+1 , 0, (struct sockaddr *)&client ,lg_client );
if ( n != strlen(bonjour)+1 ) { fprintf(stderr,
"Serveur: echec de l\'envoi du message au client\n");
perror("Serveur: echec de sendto :");
(void) close ( sockfd);
exit (-1);
} } }
JL A 2 01 5, A G 2 02 1
Exemple de client
JL A 2 01 5, A G 2 02 1
/*
Example de client UDP utilisant la fonction creer_client_udp
int creer_client_udp ( char * adresse, int port, struct sockaddr_in * pserver,
struct sockaddr_in * pclient,int debug )
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
main( int argc, char **argv ) {
int socket; /* descripteur de la socket */
size_t nombre; /* longueur message */
ssize_t n; /* retour de recvfrom ou sento */
/* structures sockaddr_in pour serveur et client */
struct sockaddr_in serveur, client;
socklen_t lserveur=(socklen_t) sizeof(serveur);
char * message_client="Bonjour serveur";
char message_serveur[256];
JL A 2 01 5, A G 2 02 1
socket = creer_client_udp ( "127.0.0.1", 9999, &serveur, &client, 1);
if ( socket < 0 ) {
fprintf(stderr,"Client: retour de la primitive socket en erreur\n");
perror("Client: pb de creation socket :");
exit(-1);
}
/* Envoi du message au serveur */
nombre = (size_t)strlen(message_client)+1;
n=sendto(socket, (void *) message_client, nombre, 0,
(struct sockaddr *) &serveur, (socklen_t) sizeof(serveur));
if ( n != (ssize_t)nombre) {
fprintf(stderr,"Client : echec de l\envoi du message\n");
perror("Clent : erreur avec sendto :");
(void) close (socket);
exit(-1);
}
JL A 2 01 5, A G 2 02 1
/* Maintenant on recoit le message du serveur */
n=recvfrom(socket, (void *) message_serveur, (size_t) 15, 0, (struct sockaddr *) &serveur, &lserveur);
if ( n != (size_t) 15 ){
fprintf(stderr, "Client : reception du message serveur incorrecte\n");
perror("Client : pb avec recvfrom :");
(void) close(socket);
exit(-1);
}
printf("Message recu du serveur : %s\n");
(void) close(socket);
exit(0);
}
JL A 2 01 5, A G 2 02 1
Exemple de Makefile
JL A 2 01 5, A G 2 02 1
.PHONY : all clean
all : serveur_tcp client_tcp serveur_udp client_udp
serveur_tcp : serveur_tcp.o ../lib/client_serveur.a(fonction_serveur_tcp.o) cc -o serveur_tcp serveur_tcp.o client_serveur.a
client_tcp : client_tcp.o client_serveur.a(fonction_client_tcp.o) cc -o client_tcp client_tcp.o ../lib/client_serveur.a
serveur_udp : serveur_udp.o client_serveur.a(fonction_serveur_udp.o) cc -o serveur_udp serveur_udp.o ../lib/client_serveur.a
client_udp : client_udp.o client_serveur.a(fonction_client_udp.o) cc -o client_udp client_udp.o ../lib/client_serveur.a
clean :
@rm -f *.o *~ serveur_tcp client_tcp serveur_udp client_udp
JL A 2 01 5, A G 2 02 1
client_serveur.a : fonction_serveur_tcp.o fonction_client_tcp.o \ fonction_serveur_udp.o fonction_client_udp.o
ar r client_serveur.a fonction_serveur_tcp.o fonction_client_tcp.o \ fonction_serveur_tcp.o fonction_client_tcp.o
ar t client_serveur.a clean :
@rm -f *.o *~ client_serveur.a serveur_tcp client_tcp serveur_udp client_udp
JL A 2 01 5, A G 2 02 1
Fonctions utiles
JL A 2 01 5, A G 2 02 1
Résolution de noms/adresse
Transformation d'une adresse symbolique en une adresse IP:
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name)
●
Transformation d'une adresse IP en un nom:
#include <sys/socket.h>
struct hostent *
gethostbyaddr(const void *addr, int len, int type)
JL A 2 01 5, A G 2 02 1
struct hostent
La structure hostent est définie dans <netdb.h>
struct hostent {
char *h_name; /* Nom officiel de l’hôte */
char **h_aliases; /* Liste d’alias */
int h_addrtype; /* Type d’adresse de l’hôte
*/
int h_length; /* Longueur de l’adresse */
char **h_addr_list; /* Liste d’adresses */
}
#define h_addr h_addr_list[0]
JL A 2 01 5, A G 2 02 1
struct hostent
h_name : nom officiel de l’hôte.
h_aliases : un tableau, terminé par un pointeur NULL, d’alternatives au nom officiel de
l’hôte.
h_addrtype le type d’adresse : actuellement toujours AF_INET ou AF_INET6.
h_length : la longueur, en octets, de l’adresse.
h_addr_list : un tableau, terminé par un
pointeur NULL, d’adresses réseau pour l’hôte, avec l’ordre des octets du réseau.
h_addr : la première adresse dans h_addr_list
pour respecter la compatibilité ascendante.
JL A 2 01 5, A G 2 02 1
Utilisation de gethostbyname
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include <endian.h>
// Programme utilisant la fonction gethostbyname // Deux appels :
// adresse nom_symbolique : affiche la premiere adresse ip de // nom_symbolique
// adresses nom_symbolique : affiche l'ensemble des adresses // ip de nom_symbolique
// Les deux appels sont realises par l'utilisation des liens
// physiques (ln arp adresse) ou symboliques (ln -s arp adresse)
// Compilation make arp, ou make CC=cc -DDEBUG arp pout inclure // les informations de deboguage
int main(int argc, char **argv){
struct hostent *adresses;
struct in_addr adresse[128];
int i, max;
JL A 2 01 5, A G 2 02 1
if(argc != 2){
printf(
"Mauvais nombre d'arguments : entrez un nom symbolique de machine\n");
exit(1);
}
if(strcmp(argv[0],"adresse") == 0) {
#ifdef DEBUG
fprintf(stderr,"argv = adresse\n");
#endif max=1;
} else {
#ifdef DEBUG
fprintf(stderr,"argv != adresse\n");
#endif
max=128;
}
adresses = gethostbyname(argv[1]);
if ( adresses == (struct hostent *) NULL) {
fprintf(stderr, "Pas de machine portant le nom %s\n",argv[1]);
exit(-1);
}
fprintf(stderr,"adresse de : ");
printf("%s\n",adresses->h_name);
JL A 2 01 5, A G 2 02 1
i=0;
while ( (adresses->h_addr_list[i] != (char *) NULL) && i<127 && i<max ) {
memcpy((char*)&((adresse+i)->s_addr),*(adresses->h_addr_list+i),adresses->h_length);
#ifdef DEBUG fprintf(stderr,
"adresses->h_addr_list[%d] en hexadecimal et en format interne : %8.8lx \n", i,(adresse+i)->s_addr);
#endif
fprintf(stderr,"Adresse IP pointee : %s\n",inet_ntoa(*(adresse+i)));
i++;
}
putchar('\n');putchar('\n');
}
JL A 2 01 5, A G 2 02 1
Obtention des identificateurs de connexion
Ces deux fonctions utilisent le descripteur de socket pour obtenir les extrémités d'une connexion.
Obtention des adresse et ports locaux:
#include <sys/socket.h>
int getsockname(int s, struct sockaddr *name, socklen_t *namelen)
namelen est un paramètre donnée/résultat contenant
à l'appel la taille de la structure, la taille du nom au
retour
JL A 2 01 5, A G 2 02 1
Obtention des adresse et port distants:
#include <sys/socket.h>
int getpeername(int s, struct sockaddr *name, socklen_t *namelen)
Même remarque que dans getsockname
concernant l'utilisation de namelen
JL A 2 01 5, A G 2 02 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
int main (int argc, char * argv[]) {
int sock, msgsock;
size_t length;
socklen_t lemis;
struct sockaddr_in name;
struct sockaddr_in emis;
char buf [1024];
int rval, i;
/* Creation socket AF_INET entre machines */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror ("socket");
exit(1);
}
JL A 2 01 5, A G 2 02 1
/* Initialisation de la socket */
bzero (&name, sizeof (name));
lemis = sizeof(emis);
bzero (&emis, lemis);
name.sin_family = AF_INET;
/* adresse source indifférente */
name.sin_addr.s_addr = INADDR_ANY;
/* port 9999 (arbitraire) */
name.sin_port = htons(9999);
length = sizeof(name);
if (bind (sock, (struct sockaddr *)&name, length) != 0) {
perror ("bind");
close (sock);
return 1;
}
JL A 2 01 5, A G 2 02 1
/* ntohs conversion entiers format réseau to hote */
printf ("Socket serveur TCP port #%d\n", ntohs (name.sin_port));
/* Début acceptation de connexion sur la socket sock Accepte 5 connexions maximum en attente */
listen(sock,5);
do
/* Si demande de connexion ouvre une socket pour celle-ci */
JL A 2 01 5, A G 2 02 1
{
msgsock = accept(sock, &emis, (int *)0);
if (msgsock < 0) perror("accept");
else do /* Récupération des références de l'émetteur pour impression */
{
if (getsockname (msgsock,(struct sockaddr *)&emis, &lemis) < 0){
perror ("getsockname");
close (sock);
return 1;
}
/* ntohs conversion entiers format réseau to hote */
printf ("Socket TCP emetteur %s port #%d\n", inet_ntoa(emis.sin_addr.s_addr),
ntohs (emis.sin_port));
bzero(buf, sizeof(buf));
if ((rval = read (msgsock, buf, 1024)) < 0){
perror("read"); i = 1;
} i = 0;
if (rval == 0)
fprintf(stderr, "Fin connexion\n");
else
fprintf(stdout,"--> %s\n",buf);
} while (rval !=0);
close (msgsock);
} while (i);
return 0;
}