• Aucun résultat trouvé

Les fonctions simplifiant l'utilisation de l'interface socket d'accès aux protocoles TCP/IP

N/A
N/A
Protected

Academic year: 2022

Partager "Les fonctions simplifiant l'utilisation de l'interface socket d'accès aux protocoles TCP/IP"

Copied!
77
0
0

Texte intégral

(1)

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

(2)

JL A 2 01 5, A G 2 02 1

TCP/IP : historique Fonctions ou primitives

des protocoles

(3)

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)

(4)

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

(5)

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

(6)

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)

(7)

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

(8)

JL A 2 01 5, A G 2 02 1

Mode Connecté

(9)

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

(10)

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

(11)

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

(12)

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

(13)

JL A 2 01 5, A G 2 02 1

Non connecté

(14)

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

(15)

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

(16)

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

(17)

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

(18)

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

(19)

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.

(20)

JL A 2 01 5, A G 2 02 1

FONCTIONS DE CRÉATION DE

CLIENT/SERVEUR

(21)

JL A 2 01 5, A G 2 02 1

Mode connecté : TCP

(22)

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

(23)

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é

(24)

JL A 2 01 5, A G 2 02 1

Attente de la connexion d'un client

(25)

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.

(26)

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.

(27)

JL A 2 01 5, A G 2 02 1

EXEMPLES

(28)

JL A 2 01 5, A G 2 02 1

Client

(29)

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;

}

(30)

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;

}

(31)

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

(32)

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.

(33)

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;

}

(34)

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;

}

(35)

JL A 2 01 5, A G 2 02 1

Serveur

Multiple clients traités en séquentiel

(36)

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;

}

(37)

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;

}

(38)

JL A 2 01 5, A G 2 02 1

Serveur

Multiple clients traités en parallèle

(39)

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);

}

(40)

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 */

(41)

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);

}

(42)

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

(43)

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);

(44)

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

(45)

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.

*/

(46)

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);

}

(47)

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();

(48)

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);

}

(49)

JL A 2 01 5, A G 2 02 1

UDP

(50)

JL A 2 01 5, A G 2 02 1

Création d'un serveur UDP

(51)

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

(52)

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

(53)

JL A 2 01 5, A G 2 02 1

Création d'un client UDP

(54)

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

(55)

JL A 2 01 5, A G 2 02 1

Exemple de serveur

(56)

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);

}

(57)

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);

} } }

(58)

JL A 2 01 5, A G 2 02 1

Exemple de client

(59)

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];

(60)

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);

}

(61)

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);

}

(62)

JL A 2 01 5, A G 2 02 1

Exemple de Makefile

(63)

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

(64)

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

(65)

JL A 2 01 5, A G 2 02 1

Fonctions utiles

(66)

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)

(67)

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]

(68)

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.

(69)

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;

(70)

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);

(71)

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');

}

(72)

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

(73)

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

(74)

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);

}

(75)

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;

}

(76)

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 */

(77)

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;

}

Références

Documents relatifs

) Les machines sont reliées entre elles dans un même domaine logiquement et non par adressage. Exemple : 10 machines d’un même domaine appartiennent à 10 réseaux différents

Pour sa déclaration d'amour, Tristan a écrit une grande lettre de 40 pages à Nadia qu'il souhaite envoyer par courrier postal?. Les enveloppes pré-timbrées qu'il utilise

c) Combien de machines chaque sous réseau pourra-t-il comporter (expliquez) ? d) Donner l’adresse du sous réseau n° 7 ? Justifier votre réponse ?. e) Quelle est l’adresse de

3) Quel est le masque réseau adéquat qui doit être choisi par l’administrateur s’il veut coder au maximum 62 hôtes ? Quel est l’intérêt d’adopter ce masque réseau au lieu

public static void main (String[] args) throws IOException {. int port; // no de port

Sinon, si la destination correspond à celui d'un réseau accessible via un routeur on récupère l'adresse physique de ce routeur et on lui transmet le paquet : routage

• Protocole TCP (Transmission Control Protocol) : régit les échanges de paquets de données entre des machines connectées sur internet en veillant à ce que tous les

• utiliser le vocabulaire spécifique des protocoles de communication en couches du modèle OSI appliqué au monde Internet et au protocole IP particulièrement,.. • expliquer