Programmations des Sockets Benmoussa Yahia Université M hamed Bougara de Boumerdès Yahia.benm@gmail.com
Objectifs Différencier entre les modes de transport orientés connexion et non-orientés connexion Comprendre l utilité des numéros de ports utilisés dans TCP & UDP Utiliser les sockets pour écrire des applications réseaux au dessus de la pile de protocoles fournie par le système d exploitation Comprendre la difficulté d écrire des applications réseaux en utilisant les sockets 2
TCP/IP Une suite de protocoles ayant comme objectif de fournir un outil universel d interconnexion à travers des réseaux physiques hétérogènes TCP & IP sont les protocole les plus importants mais existent d autre protocoles dans la suite TCP/IP : UDP : protocole de transport FTP, TELNET, SSH, SMTP, HTTP, etc : protocoles de la couche application. 3
TCP/IP & le modèle OSI Application Présentation Session Transport Réseau Liaison Physique ftp, telnet, ssh, smtp, http, etc TCP/UDP IP TCP/IP 4
Les protocoles de transport TCP et UDP Protocole de transport point-à-point Permettent le transport de données entre applications distantes Dans une communication point-à-point, plusieurs application de chaque coté échangent des données simultanément La séparation entre les données des différentes applications est garantie par l utilisation de numéro de ports distincts 5
Les protocoles de transport TCP et UDP Pour transférer des fichiers, j utilise FTP sur le port 21 Pour me connecter à distance, j utilise SSH sur le port 22 Pour envoyer des e-mails, j utilise SMTP sur le port 25 Pour consulter le WEB, j utilise HTTP sur le port 80 Vers serveur FTP Vers serveur SSH Vers serveur SMTP Vers serveur HTTP TCP/UDP IP TCP/UDP IP 6
Les protocoles de transport TCP et UDP Numéro de port dans un segment TCP 7
Les protocoles de transport TCP et UDP Numéro de port dans un segment UDP 8
TCP vs UDP TCP Garantit la transmission des données Garantit l ordre de transmission UDP Ne garantit la transmission des données Ne garantit l ordre de transmission 9
Mise en œuvre des applications réseaux Implémentation des applications réseaux au dessus de systèmes d exploitation réseaux Les fonctionnalités réseaux sont implémenté au niveau d une pile de protocoles L accès à la pile de protocole se fait via une API 10
Mise en œuvre des applications réseaux Application réseau Application réseau Application réseau API Système Système d exploitation Système d exploitation Système d exploitation Pile de protocoles Réseau de communication 11
Les Sockets Interface de programmation réseau générique Introduite la première fois dans UNIX BSD 4.2 Dans Unix/Linux, une socket est comme un descripteur de fichier sur lequel on effectue des opérations de Lecture/Ecriture classiques Conçues pour utiliser différentes familles de protocoles réseaux et différents types de protocoles de transport 12
Familles de protocoles AF_UNIX, AF_LOCAL : communication interprocessus local AF_INET : Communication via ipv4 AF_INET6 : Communication via ipv6 AF_IPX : Communication via IPX de Novell AF_APPLTALK... 13
Familles de protocoles AF_UNIX, AF_LOCAL : communication interprocessus local AF_INET : Communication via ipv4 AF_INET6 : Communication via ipv6 AF_IPX : Communication via IPX de Novell AF_APPLTALK... 14
Type de sockets SOCK_STREAM Mode de transport orienté connexion Le choix du type STREAM force l utilisation de TCP SOCK_DGRAM Mode de transport non-orienté connexion Le choix du type DGRAM force l utilisation de UDP 15
Création d une Socket (1) Création d un descripteur pour la socket int socket (int sa_familly, int type, int protocol); sa_familly : AF_UNIX, AF_INET, type: SOCK_STREAM, SOCK_DGRAM, protocole: mis à 0 pour un choix automatique Valeur de retour : descripteur de la socket si créé sans erreur sinon -1 16
Initialisation de l adresse d une socket AF_INET (2) L adresse d une socket dépend de la famille de Protocol utilisé. Dans le cas AF_INET, c est une structure sockaddr_in (voir man 7 ip) struct sockaddr_in { sa_family_t sin_family; /* famille : AF_INET */ in_port_t sin_port; /* N port au format réseau */ struct in_addr sin_addr; /* l @ IP au format réseau */ }; struct in_addr { in_addr_t s_addr; }; 17
Initialisation de l adresse d une socket AF_INET (2) struct sockaddr_in sa; sa.sin_family=af_inet; sa.sin_port=htons(21); // FTP sa.sin_addr.s_addr=htonl(0x7f000001); //127.0.0.1 Si NUMERO_PORT=0 alors le système choisit un port aléatoire. Si ADRESSE_IP= INADDR_ANY alors la socket écoute sur toutes adresse disponibles dans le système Si ADRESSE_IP= INADDR_LOOPBACK alors la socket écoute sur l adresse de bouclage. 18
Initialisation de l adresse d une socket AF_INET Représentation réseau vs représentions hôte (Big andian & Little andian) Dans les CPU Intel, les entiers sont stockés en mémoire en commençant par l octet le moins significatif Dans les CPU Motorola, les entiers sont stockés en mémoire en commençant par l octet le plus significatif Pour lever toute ambiguïté, on utilise un ensemble to fonction de conversion (voir man byteorder) htons() host to network short htonl() host to network long ntohs() network to host short stohl() short network to host long 19
Initialisation de l adresse d une socket AF_INET (2) Bits plus significatifs Bits moins significatifs 192.168.196.1=11000000.10101000.11000100.00000001 11000000 10101000 11000100 00000001 00000001 11000100 10101000 11000000 CPU Motorola CPU Intel 20
Initialisation de l adresse d une socket AF_INET (2) Bits plus significatifs Bits moins significatifs 192.168.196.1=11000000.10101000.11000100.00000001 11000000 10101000 11000100 00000001 00000001 11000100 10101000 11000000 CPU Motorola CPU Intel 21
Initialisation de l adresse d une socket AF_INET (2) Dans le cas ou l adresse IP est disponible sous format chaine de caractères, on utilise inet_addr() inet_addr() permet de convertir une adresse IP au format chaine de caractère au format binaire réseau (Big andian) La fonction qui convertit une adresse du format réseau en un chaine de caractère est inet_ntop() 22
Communication Client/Serveur Chacun des client et du serveur doivent créer une socket Le serveur initialise l adresse de la socket sur laquelle il va écouter les requêtes entrantes et le client initialise l adresse de la socket du serveur qu il va contacter Le scénario de communication dépend par la suite du mode de transport de données (orienté connection/ non-orienté connexion) 23
Client/Serveur en mode connecté 1- Création s1=socket 2- Attachement bind() socket() 1- Création 3- Attente de connexions listen() connect() 2- Initiation de connexion 4- Accepter une connexion s2=accept() write() 3- Envoyer des données 5- Lire des données Read() read() 4- Lire des données 6- Envoyer des données Write() Close() 5- Fermer la socket 4- Fermer la socket Close() 24
Client/Serveur en mode non-connecté 1- Création s1=socket 2- Attachement bind() socket() sendto() 3- Attente de messages recvefrom() recvefrom() 3- Envoie de messages sendto() Close() 4- Fermeture de la socket Close() 25
Attachement d une socket à une adresse (2) Opération effectué coté serveur (dans les deux modes) int bind(int sfd, struct sockaddr *sd, socklen_t addrlen); bind retourne 0 si l attachement de la socket est réussie sinon elle retourne -1 26
Création d une file d attente de connections (3) Opération effectuée du coté serveur en mode connecté seulement Cette opération définit la taille de la files d attente int listen(int s, int taille_file_attente); 27
Accepter les connexion (4) Opération effectuée du coté serveur en mode connecté seulement Cette opération retire la connexion de la file d attente et récupère l adresse de la socket paire (du client) int accept(int sock, struct sockaddr *adresse, socklen_t *longueur); 28
Initiation d une connexion par un client (5) Opération effectuée du coté client en mode connecté seulement int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen); 29
Envoie/Réception de données (6) read() / write() reste valide recv() / sendto() sont plus adapté au sockets recvfrom() / sendto() sont utilisé en mode nonconnecté Voir les pages de manuels pour plus de détails 30
Ou utiliser quelle fonction? Mode de connexion Etablissement de la connexion Envoie Réception Serveur connecté bind() send() recv() listen() sendto() recvfrom() accept() write() read() Client connecté connect() send() recv() sendto() recvfrom() write() read() Serveur non-connecté bind() sendto() recvfrom() Client non-connecté N/A sendto() recvfrom() 31
/* Serveur TCP */ 1. #define MAX_SIZE 128 2. int main () { 3. int dfs_serveur, dfs_client, long_adr_serveur, long_adr_client; 4. int struct sockaddr_in adresse_serveur, adresse_client; 5. char buf[max_size]; 6. dfs_serveur = socket (AF_INET, SOCK_STREAM, 0); // Création de la socket 7. adresse_serveur.sin_family = AF_INET; // Initialisation 8. adresse_serveur.sin_addr.s_addr=inet_addr ("127.0.0.1 ") ; // de l'adresse 9. adresse_serveur.sin_port = htons(1234); // de la socket 10. long_adr_serveur = sizeof (adresse_serveur); // Association de l'adresse 11. bind (dfs_serveur, (struct sockaddr *) &adresse_serveur,long_adr_serveur); // à la socket 12. listen (dfs_serveur, 5); // Création d'une file d'attente de connexions pour les clients. 13. while (1) 14. { 15. // Accepter les connections 16. dfs_client = accept (dfs_serveur, (struct sockaddr *) &adresse_client,&long_adr_client); 17. read (dfs_client, buf, MAX_SIZE); // Lecture des données envoyées par le client 18. printf ("%s\n", buf); 19. close (dfs_client); 20. } 21. } 32
/* Client TCP */ 1. int main () 2. { 3. int dfs_serveur; 4. int len; 5. struct sockaddr_in adresse_serveur; 6. int result; 7. sin_addr.s_addr 8. dfs_serveur = socket (AF_INET, SOCK_STREAM, 0); // Création de la socket du client 9. adresse_serveur.sin_family = AF_INET; // initialisation 10. adresse_serveur.sin_addr.s_addr = inet_addr ("127.0.0.1"); // de l'adresse 11. adresse_serveur.sin_port = htons (1234); // du serveur 12. len = sizeof (adresse_serveur); 13. result = connect (dfs_serveur, (struct sockaddr *) &adresse_serveur, len); // Connection au serveur 14. write (dfs_serveur, "Hello word", 10); // Envoie de message au serveur 15. close (dfs_serveur); 16. exit (0); 17. } 18. 33
Gestion des connexions Clients Un serveur doit gérer les requêtes de plusieurs clients Plusieurs techniques : Serveur itératif Serveur multiprocessus Serveur multithread 34
Gestion des connexions Clients Serveur itératif Simple Mais Gestion séquentielle des clients (non parallèle) Le serveur est souvent en attente bloquante L attente non bloquante avec sockfd = socket(af_inet, SOCK_STREAM, 0); fcntl(sockfd, F_SETFL, O_NONBLOCK); utilise excessivement la CPU 35
Gestion des connexions Clients Serveur multiprocessus 36
/* Serveur TCP */ 1. #define MAX_SIZE 128 2. int main () { 3. int dfs_serveur, dfs_client, long_adr_serveur, long_adr_client; 4. int struct sockaddr_in adresse_serveur, adresse_client; 5. char buf[max_size]; 6. dfs_serveur = socket (AF_INET, SOCK_STREAM, 0); // Création de la socket 7. adresse_serveur.sin_family = AF_INET; // Initialisation 8. adresse_serveur.sin_addr.s_addr=inet_addr ("127.0.0.1 ") ; // de l'adresse 9. adresse_serveur.sin_port = htons(1234); // de la socket 10. long_adr_serveur = sizeof (adresse_serveur); // Association de l'adresse 11. bind (dfs_serveur, (struct sockaddr *) &adresse_serveur,long_adr_serveur); // à la socket 12. listen (dfs_serveur, 5); // Création d'une file d'attente de connexions pour les clients. 13. while (1) 14. { 15. / Accepter les connections 16. dfs_client = accept (dfs_serveur, (struct sockaddr *) &adresse_client,&long_adr_client); if ( (childpid = fork() == 0) { /* Processus fils */ 17. close((dfs_serveur); /* close listening socket */ 18. read (dfs_client, buf, MAX_SIZE); 19. exit 0); 20. } 21. close(dfs_client); /* Le processus parent ferme la socket associé au client */ 22. }}} 37
Gestion des connexions Clients Serveur multiprocessus Domaine d exécution séparé pour chaque client Sécurité, Robustesse, Programmation facile Mais Le fork() est couteux (Temps et Mémoire) Nécessite des mécanisme IPC pour faire communiquer les différent processus serveurs 38
Gestion des connexions Clients Serveur Multithread 39
/* Serveur TCP */ 1. #define MAX_SIZE 128 2. int main () { 3. int dfs_serveur, dfs_client, long_adr_serveur, long_adr_client; 4. int struct sockaddr_in adresse_serveur, adresse_client; 5. char buf[max_size]; 6. dfs_serveur = socket (AF_INET, SOCK_STREAM, 0); 7. adresse_serveur.sin_family = AF_INET; 8. adresse_serveur.sin_addr.s_addr=inet_addr ("127.0.0.1 ") ; 9. adresse_serveur.sin_port = htons(1234); 10. long_adr_serveur = sizeof (adresse_serveur); 11. bind (dfs_serveur, (struct sockaddr *) &adresse_serveur,long_adr_serveur); 12. listen (dfs_serveur, 5); // Création d'une file d'attente de connexions pour les clients. 13. while (1) 14. { 15. long_adr_client = sizeof (adresse_client); 16. dfs_client = accept (dfs_serveur, (struct sockaddr *) &adresse_client,&long_adr_client); 17. pthread_create(null, NULL, &server_thread, (void *) dfs_client): } 18. } void * server_thread(void *arg) { read ((int)arg, buf, MAX_SIZE); printf ("%s\n", buf); close ((int) arg); /* Il faut pas oublier de gérer les accès conçurent à la variable buf */ } 40
Gestion des connexions Clients Serveur Multithread Performance Supporte un nombre important de clients Communication facile à l aide de variables Mais Nécessité d une gestion explicite de l exclusion mutuelle entre les threads. La défection d un thread entraine la défection des autre. 41
Références Unix Network Programming Vol.1. R. Stevens Beej's Guide to Network Programming Using Internet Sockets. www.beej.us Beginning Linux Programming. Neil Matthew, Richard Stones. TCP/IP Tutorial and Technical Overview http://www.redbooks.ibm.com/ 42