TP1 et TP2 - cours

TP Réseaux Informatiques: Socket TCP en C
But du TP
Se familiariser avec l’API socket sous Windows. On s’intéresse au mode connecté TCP.
#
Pour la programmation, utiliser l’environnement Codeblocks. Pour chaque partie du TP,
créer un nouveau projet. Ne pas changer les les noms de variable données dans les codes à
compéter. A la fin, n’oublier pas de sauvegarder votre travail dans votre espace personnel
ou sur un support externe. Les squelettes du code sont téléchargeables à partir de l’adresse :
nehsetl.free.fr ou sur e-media
"
!
Les chargés de TP ne déboguent pas les erreurs de programmation. Vous devriez connaître
les notions de base de la programmation en langage C.
Partie I
Dans cette partie, il s’agit de développer un serveur Echo. Le serveur reçoit un message du client et il le renvoit
aussi tôt au client. On s’interesse dans ce TP au mode connecté, c’est-à-dire, au socket TCP (le mode non connecté
UDP a été vu en Licence 3). L’exécution du client et du serveur se fera chacune sur une console cmd différente :
lancer le serveur
server port (exemple : server 5000)
lancer le client
client @IP_serveur port (exemple : client 127.0.0.1 5000)
Puisque l’@IP et le numéro de port sont désignés en ligne de commande, vous deviez savoir comment récupérer
des argument dans un programme en C !
Travail à faire :
1. Remplir les deux squelettes client.c et server.c (créer un projet pour chaque squelette).
2. Exécuter le serveur en premier ensuite, le client. Faire un essai en local, puis avec vos collègues.
3. Essayer de connecter un deuxième client, que se passe-t-il ?
4. Quel est le rôle des bibliothèques suivantes : stdlib.h, type.h, winsowk2.h et ws2tcpip.h.
5. Quel est le rôle des deux premières lignes du code : WSADATA et WSAStartup ?
1
Partie II
Dans la première partie, un serveur itératif a été développé. Itératif ? c’est-à-dire, un seul client est servi à la
fois. Et pour servir plusieurs clients ? Deux solutions :
• concurrence : consiste à créer un nouveau processus pour servir chaque demande de connexion. Dans ce cas,
un processus principal attend au niveau de la fonction accept. Lorsqu’il reçoit une demande de connexion, il
crée un processus fils qui va interagir avec le client. Le processus principal se met de nouveau en attente au
niveau de la fonction accept. Cette méthode est aussi appelée fork et exec (vous en savez quelques chose
cette année dans le cours programmation système).
• multiplexage : en utilisant la fonction select. C’est la solution qui nous intérèsse.
Travail à faire :
1. faire une recherche bibliographique sur la méthode select.
2. Compléter le squelette serverSelect.c.
3. Pour le test, vous aurez besoin de trois consoles cmd, une pour lancer le serveur et les deux autres pour lancer
deux clients.
4. Expliquer et commenter le code.
lancer le serveur
server port (exemple : server 5000)
lancer le 1er client
client @IP_serveur port (exemple : client 127.0.0.1 5000)
lancer le 2eme client
client @IP_serveur port (exemple : client 127.0.0.1 5000)
Partie III
Modifier le code développé dans la section précédente pour faire un serveur Echo multi-clients.
2
1
Introduction
• Socket ? une abstraction à travers laquelle une application peut envoyer et recevoir des données. C’est un point d’accès permettant
à un processus de communiquer avec d’autres processus.
• Client/Serveur ? le client est demandeur de servcies. Le serveur fournit les services.
Serveur
Client
• Port ? il permet d’atteindre un processus particulier. Une socket est donc une adresse IP et un numéro de port : @IP :Port.
@IP :N_port
@IP :N_port
172.21.110.2 :45000
194.199.25.10 :5000
socket client
socket serveur
Serveur
Client
• TCP/UDP ? : les sockets fournissent une interface d’accès, à partir de la couche application, aux interfaces de la couche
Transport (modèle OSI ou TCP/IP). Deux protocoles principales :
TCP : mode connecté. Une liaison est établie au préalable entre le client et le serveur. Les données échangées sont de type flot
(stream).Exemple : téléphonie.
UDP : mode non connecté. Aucune liaison n’est établie entre le client et le serveur. Les données échangées sont de type
datagrammes. Exemple : pas besoin d’établir une connexion pour envoyer un courrier via la poste.
• Y a-t-il une différence entre un client et un serveur ? Oui, Chacun utilise l’interface socket différemment. Ci dessous
l’algorithme à respecter. On s’interesse seulement au mode connecté :
Client
créer une socket: socket()
établir une connexion avec le serveur: connect()
envoi et récépetion des données: send() et recv()
fermer le socket: close()
Serveur
créer une socket: socket()
assigner un numéro de port: bind()
écouter les requêtes de connexion à travers le port assigné: listen()
répéter:
appeler accept() pour créer un nouveau socket par client connecté
envoi et réception de données via le nouveau socket: send() et recv()
fermer le socket client: close()
2
API Socket
2.1
Socket serveur
1. Créer une socket
i n t s o c k e t ( i n t domain , i n t type , i n t p r o t o c o l )
socket serveur
Serveur
• domain : domaine de connexion. AF_INET pour à IPv4.
• type : mode de connexion. SOCK_STREAM (TCP : mode connecté), SOCK_DGRAM (UDP : mode non connecté)
• protocol : protocole point-à-point. Pour IPv4, 0 par défaut.
2. Associer socket à une adresse et un port. Un client doit spécifier l’adresse et le port du serveur. Le serveur utilise aussi
l’adressage pour reconnaitre l’ensemble des clients connectés à travers son port. Cette étape revient à remplir une structure
d’addressage sockaddr :
3
struct s o c k a d d r { u_short s a _ f a m i l y ; char sa_data [ 1 4 ] ; } ;
• sa_family : famille d’adresse. AF_INET pour à IPv4.
• sa_data : information supplémentaire sur la famille d’adresse.
La structure sockaddr est une structure générique. pour IPv4, on utilise plutôt une structure particulière : sockaddr_in.
struct so ckad dr_ in {
short
sin_family ;
u_short s i n _ p o r t ;
struct in_addr sin_addr ;
char
sin_zero [ 8 ] ;
};
• sin_family : AF_INET.
• sin_port : numéro de port (16 bits).
• sin_addr : une structure contenant une adresse IPv4 (32 bits). sin_addr est une structure de type in_addr :
struct in_addr { u_long s_addr ; } ;
• sin_zero : 0 (non utilisé).
3. Ensuite, une fois la structure d’adresse est remplie, le client et le serveur se donne rendez-vous à l’adresse et au port du serveur.
C’est le rôle de bind().
port
socket serveur
Serveur
i n t bind ( i n t s o c k e t , struct s o c k a d d r ∗ l o c a l A d d r e s s , i n t a d d r e s s S i z e )
• localAddress : la structure de l’adresses locale du serveur.
• addressSize : spécifie la taille de la structure.
4. Ecoute des clients : une socket serveur est en attente de demandes de connexion d’un ou de plusieurs clients.
int l i s t e n ( int socket , int queueLimit )
• queueLimit : spécifie le nombre maximal des connexions en attente. En effet, si une demande arrive pendant qu’une autre est
en cours de traitement, elle est placée dans une file d’attente. Si une demande arrive alors que la file est pleine, elle est rejetée.
socket serveur
file
Serveur
5. Acceptation d’une demande de connexion : le serveur décide d’accepter une connexion. Il utilise la fonction accept().
i n t a c c e p t ( i n t s o c k e t , struct s o c k a d d r ∗ c l i e n t A d d r e s s , i n t ∗ a d d r e s s L e n g t h )
• socket : descripteur du socket client.
• clientAddress : structure d’adresse du client.
• addressLength : taille de la structure d’adresse du client.
Cette primitive est bloquante. elle permet d’extraire la connexion de la pile d’attente. Elle permet aussi de remplir, automatiquement, la structure clientAddress avec l’adresse et le port du client.
Attention ! la socket serveur utilisée pour l’écoute (qui a été assignée par bind()) n’est jamais utilisée pour la communication.
elle est juste utilisée pour créer une nouvelle socket pour chaque client connecté. Ensuite, le serveur communique via cette nouvelle
socket comme le montre la figure suivante :
socket serveur
demande
file
creation
connexion
nouveau socket
Serveur
4
2.2
Socket client
1. La création d’une socket client se fait de la même manière que le serveur.
2. Connecter une socket : établir une connexion entre socket client et socket serveur (c’est le principe de mode connecté).
i n t c o n n e c t ( i n t s o c k e t , const struct s o c k a d d r ∗ s e r v e r A d d r e s s ,
int addressLength )
• socket : descripteur du socket client.
• serverAddress : la structure contenant l’@IP et le ports du serveur.
• addressLength : spécifie la taille de la structure d’adresse du serveur.
socket serveur
connect
creation
connexion etablie
client
2.3
socket client
nouveau socket
Serveur
Envoi et réception
i n t send ( i n t s o c k e t , char ∗msg , i n t msgLength , i n t f l a g s )
i n t r e c v ( i n t s o c k e t , char ∗ r c v B u f f e r , i n t b u f f e r L e n g t h , i n t f l a g s )
•
•
•
•
socket : descripteur du socket connecté.
msg : un pointeur vers le message à envoyer.
msgLength : la longueur en octet du message à envoyer.
rcvBuffer : pointeur sur le tampon de réception. Le tempon est une zone de mémoire ou les données sont stockées avant d’être
traitées.
• bufferLength : la taille du tampon.
• flags : Par défaut égal à 0.
Par défaut l’appel à read ou recv est bloquant. S’il n’y a rien a lire, on attend indéfiniment.
3
Multiplexage
L’appel système qui permet le multiplexage est select. select examine les descripteurs qui lui sont passés en paramètre et teste si
certains sont prêts à lire ou à écrire.
int s e l e c t ( int socket , fd_set ∗ readfds , fd_set ∗ w r i t e f d s ,
f d _ s e t ∗ e x c e p t f d s , struct t i m e v a l ∗ t i m e o u t )
• les arguments 2,3 et 4 sont des pointeurs sur des structures qui contienderont les descripteurs de sockets à tester en lecture,
écriture et en réception d’exception.
• timeout : spécifie l’intervalle de temps maximal à attendre avant de sortir de select.
Avant d’appeler select, il faut initialiser ses arguments :
FD_ZERO( f d _ s e t &f d s e t )
void FD_SET( i n t fd , f d _ s e t &f d s e t )
void FD_CLR( i n t fd , f d _ s e t &f d s e t )
•
•
•
Après
FD_ZERO : intialise la structure fdset.
FD_SET : ajoute le descripteur fd à fdset.
FD_CLR : retire le descripteur fd à fdset.
select, la fonction FD_ISSET permet de savoir si le descripteur fd est sélectionné dans fdset ou non.
void FD_ISSET( i n t fd , f d _ s e t &f d s e t )
3.1
Procédures utilies
Pour que les programmes soient portables sur toutes les machines il faut pouvoir convertir les représentations réseaux en représentation machine et vice et versa.
unsigned long i n t i n e t _ a d d r ( const char ∗ adrIP )
char ∗ i n e t _ n t o a ( struct in_addr i n )
Pour que le serveur accepte toute adresse IPv4, on met le champ s_addr à INADDR_ANY.
5