You are on page 1of 30

Toumanari – ENSA3

2020-2021
Les sockets
Les sockets
• Les sockets : primitives de la couche transport.

• fournit les primitives pour le support des communications


reposant sur toute suite de protocoles; les protocoles
TCP/IP sont à l’origine des développements.

• Les applications cliente et serveur ne voient les couches


de communication qu’à travers l’API socket (abstraction):
Les sockets
Protocole Applicatif
Application cliente Application : serveur

API Socket API Socket

UDP TCP UDP TCP

IP IP

Physique Physique

4
Sockets : l’abstraction
• comme un descripteur de fichier dans le système UNIX,
• associe un descripteur à un socket;
• le concepteur d’application utilise ce descripteur pour référencer la communication
client/serveur sous-jacente.
• une structure de données «socket» est créée à l’ ouverture de socket;

Family:
table de
descripteur Service:
de fichiers Local IP:
Remote IP:
Local Port:
Remote Port:
Table de descripteurs
de processus
Structure Socket
Les Sockets : Mode connecté

SERVEUR MODE CONNECTE CLIENT

socket En mode connecté il y a établissement


(listen,connect, accept) puis libération (close)
d’une connexion entre le cleint et le serveur.
bind

listen socket
connexion
accept connect
requête
read write
réponse
write read

close
close
6
Les Sockets : primitives
La primitive socket:
– point qui permet à l’application d’obtenir un lien de communication vers
la suite de protocole qui servira d’échange,
– définit le mode de communication utilisé (connecté ou non-connecté).

La primitive bind:
- permet de spécifier le point de terminaison local (essentiellement le port
TCP/UDP dans l’environnement TCP/IP).

la primitive connect:
– permet à un client d’établir une communication active avec un serveur,
– le point de terminaison distant (adresse IP + port TCP/UDP dans
l’environnement TCP/IP) est spécifié lors de cet appel.
Les Sockets : primitives
la primitive listen :
– permet à un serveur d’entrer dans un mode d’écoute de communication ,
– dés lors le serveur est « connectable » par un client,
– le processus est bloqué jusqu’à l’arrivée d’une communication entrante.
la primitive accept :
– permet à un serveur de recevoir la communication entrante (client),
– crée un nouveau socket et retourne le descripteur associé à l’application.
– le serveur utilise ce descripteur pour gérer la communication entrante
– le serveur utilise le descripteur de socket précédent pour traiter la prochaine
communication à venir.
les primitives read et write:
– Lorsque la communication est établie, client et serveur échangent des données
afin d’obtenir (client) et transmettre (serveur) le service désiré.
la primitive close :
• termine la connexion et libère le socket associé.

8
Domaine d’un socket

 Type abstarit :
struct sockaddr {
u_short sa_family;
char sa_data[14];
};

 Domaine UNIX : AF_UNIX <sys/un.h>

struct sockaddr_un {
short sun_family; /* AF_UNIX */
char sun_path [108] /* référence UNIX */
};
Domaine d’un socket

 Domaine internet : AF_INET

struct sockaddr_in {
short sin_family; /* AF_INET */
u_short sin_port; /* numéro de port */
struct in_addr sin_addr; /* adresse internet */
char sin_zero[8]; /* champ de 8 zeros] */
};

struct in_addr {
in_addr_t s_addr;
};
in_addr_t est un type 32 bits non signés
Création d’un socket

int socket (domaine, type, protocole)

int domaine; /* AF_INET, … */


int type; /* SOCK_DGRAM, … */
int protocole; /* 0 : protocole attribué par le système*/
Types de sockets

• SOCK_DGRAM : transport de données en mode non connecté.


Dans le domaine AF_INET, le protocole UDP est utilisé.

• SOCK_STREAM : transport de données en mode connecté.


Dans le domaine AF_INET, utilisation de TCP. Possibilité d’envoi
de messages urgents.

• SOCK_RAW : permet l’accès au service de niveau 3


(émission/réception de paquets IP)
Binding : association socket/adresse
int bind (sock, p_adresse, lg)
int sock; /* descripteur de socket */
struct sockaddr *p_addresse; /* pointeur sur l’adresse */
int lg; /* longueur de l’adresse */

• Après sa création, un socket est accessible par son descripteur. Seuls


des processus fils peuvent en hériter.
• bind fournit un procédé de nommage d’un socket permettant à d’autres
processus d’y accèder.
• => si le champ sin_port de la structure sockaddr_in n’a pas été initialisé,
le numéro de port est attribué par le système. Le processus utilisera
getsockname() pour le connaître
Listen( )
• Après avoir créé un socket de type SOCK_STREAM, le serveur
indique au système qu’il est à l’écoute sur un port TCP

int listen ( sock, nb )


int sock; /* descripteur du socket d’écoute */
int nb; /* nombre maxi de connexions */
Accept()
• Accept() permet d’extraire une demande de connexion parmi
l’ensemble des requêtes en attente (FIFO)
• La primitive crée un nouveau socket qui est associée à un nouveau
numéro de port choisi par le système et retourne son descripteur

int accept ( sock, p_adr, p_lgadr )


int sock; /* descripteur du socket */
struct sockaddr *p_adr; /* adresse du client */
int *p_lgadr; /* pointeur sur la longueur de l’adresse */

=> s’il n’y a pas de requête en attente l’appel est bloquant

Au retour de l’appel, le champ p_adr->sin_port contient le numéro de


port du socket du client ayant demandé l’ouverture d’une connexion
Connect( )
• La primitive connect() réalise l’établissement d’une connexion
bidirectionnelle de bout-en-bout entre le client et le serveur en
associant socket serveur et socket client.
int connect ( sock, p_adr, lgadr )
int sock; /* descripteur socket local */

struct sockaddr *p_adr; /* adresse socket distant */


int lgadr; /* longueur adresse distante */

=> socket local et distant doivent être de même type et appartenir


au même domaine.
=> le serveur doit avoir effectué un listen() et ne pas avoir atteint la
file de demandes de connexion en attente ne doit pas être pleine.
Emission/Réception de messages TCP(1)
• Les primitives standard de lecture/écriture de fichiers permettent
de lire/écrire dans les sockets, donc d’envoyer des messages
TCP

int write ( sock, msg, lg )


int sock; /* descripteur du socket local */
char *msg; /* adresse du message */
int lg; /* longueur du message */

int read ( sock, msg, lg )


int sock; /* descripteur du socket local */
char *msg; /* adresse de sauvegarde du message */
int lg; /* longueur de la zône */
Emission de messages TCP(2)

• La primitive send permet l’envoi de données urgentes

int send (sock, msg, lg, option)


int sock; /* descripteur du socket local */
char *msg; /* adresse du message à envoyer */
int lg; /* longueur du message */
int option /* 0 ou MSG_OOB (urgence) */

=> retourne le nombre de caractères émis


Réception de messages TCP(3)

• La primitive recv permet d’extraire des données urgentes et de lire


des données sans les extraire du buffer de réception du socket

int recv ( sock, msg, lg, option )


int sock; /* descripteur du socket local */
char *msg; /* adresse de sauvegarde du message */
int lg; /* longueur de la zone */
int option; /* 0 ou MSG_PEEK ou MSG_OOB */

=> La lecture est par défaut bloquante.


Terminaison d’une connexion

• Au lieu d’utiliser la primitive close sur le descripteur du fichier


comme pour les sockets UDP, il faut utiliser la primitive shutdown
qui assure le transfert des messages en attente d’émission dans les
buffers locaux

int shutdown ( desc, sens )


int desc; /* descripteur de socket */
int sens; /* 0, 1 ou 2 */

 0 indique que le processus ne veut plus recevoir, 1 qu’il ne veut


plus émettre et 2 ni recevoir, ni émettre
 si un processus tente d’écrire sur un socket qui a été fermé en
réception sur le site distant, il reçoit le signal SIGPIPE
Socket : exemple de serveur itératif
int sockfd, newsockfd ;

if ( ( sockfd = socket (.....)) < 0 ) err_sys(«erreur de socket«) ;


if ( bind ( sockfd, ....) < 0 ) err_sys («erreur de bind»)
if ( listen ( sockfd , 5) < 0 ) ; err_sys (« erreur de listen» ) ;

for ( ; ; ) {
newsockfd = accept ( sockfd, .....) ;
if ( newsockfd < 0)
err_sys( «erreur de accept») ;

execute_la_demande( newsockfd ) ;
close ( newsockfd ) ;
}
Socket : exemple de serveur parallèle

• Primitives associèes au différentes étapes de la communication :

Création et attachement du socket


Socket/bind

Ouverture du service
listen

Attente de la demande de connexion


accept

Création d’un processus fils Traitement de la demande


processus père fork exec
processus fils
Socket : exemple de serveur parallèle
int sockfd, newsockfd ;

if ( ( sockfd = socket (.....)) < 0 ) err_sys(«erreur de socket«)


;
if ( bind ( sockfd, ....) < 0 ) err_sys («erreur de bind»)
if ( listen ( sockfd , 5) < 0 ) ; err_sys (« erreur de listen» )
;

for ( ; ; ) {
newsockfd = accept ( sockfd, .....) ;
if ( newsockfd < 0) err_sys( «erreur de accept») ;
if ( fork() == 0 ) {
close ( sockfd ) ;
execute_la_demande( newsockfd ) ;
exit (1) ; }
close ( newsockfd ) ;}
Sockets : serveurs multi-services
A fork
p processus processus
p primaire secondaire
l processus
fork
i secondaire
c exec
a exec
t code
dédié code
i dédié
o
n

O
S sockets : un par service sockets : un par connexion
Les Sockets : Mode non connecté
SERVEUR MODE NON CONNECTE CLIENT

socket socket

bind requête sendto

recvfrom

sendto

réponse
close

25
Emission de datagrammes(1)

int sendto(sock, ms, lg, option, p_dest, lgdest)


int sock; /* descripteur du socket */
char *msg; /* adresse du message à envoyer */
int lg; /* longueur du message */
int option; /* =0 pour le type SOCK_DGRAM */
struct sockaddr *p_dest; /* pointeur sur adresse socket dest */
int lgdest; /* longueur adresse socket dest */

=> retour = nombre de caractères envoyés ou –1 en cas d’échec


(descripteur ou adresse du socket de destination invalide, taille de
message ne permettant pas l’envoi d’un seul datagramme)
Réception de datagrammes (1)

int recvfrom ( sock, msg, lg, option, p_exp, p_lgexp)


int sock; /* descripteur du socket */
char *msg; /* adresse mémoire du message reçu */
int lg; /* taille du buffer contenant le message */
int option; /* 0 ou MSG_PEEK*/
struct sockaddr *p_exp; /* adresse de l’expediteur */
int *p_lgexp; /* sizeof(p_exp) et longueur du résultat */

 Le paramètre lg doit correspondre à la taille du message reçu, sinon des


caractères sont perdus (il faut connaître la taille du message envoyé)
 Retourne le nombre de caractères reçus ou –1 en cas d’erreur
Port choisi??????

• Pour accéder à l’adresse associée à un socket en connaissant


seulement son descripteur :

int getsockname (sock, p_adr, p_lg)


int sock; /* descripteur du socket */
struct sockaddr_in *p_addr; /* pointeur sur l’adresse */
int *p_lg; /* pointeur sur la longueur de l’adresse */
gethostbyname et getservbyport
struct hostent *gethostbyname(const char *name)
#include <netdb.h>

struct hostent { // #include <netdb.h>


char *h_name; // official name of host
char **h_aliases; // a zero-terminated array of alternative names
int h_addrtype; // type of adress (toujours AF_INET)
int h_length; // length of adress in bytes
char **h_addr_list; // zero-terminated array of network ardresses
h_addr; // #define h_addr h_addr_list[0]
}

struct servent *getservbyport(int port, const char *proto)


#include <netdb.h>

struc servent { // #include <netdb.h>


char *s_name; // official service name
char **s_aliases; // zero terminated list of alternative names
int s_port; // port number
char *s_proto; // the protocol to use
}
Sockets : Byte ordering
– htonl : host to network long : convertit une valeur sur 32 bits de la représentation
machine vers la représentation réseau.

– htons : host to network short : convertit une valeur sur 16 bits de la


représentation machine vers la représentation réseau.

– ntohl : network to host long : convertit une valeur sur 32 bits de la


représentation réseau vers la représentation machine.

– ntohs : network to host short : convertit une valeur sur 16 bits de la


représentation réseau vers la représentation machine.

30

You might also like