0% ont trouvé ce document utile (0 vote)
4 vues37 pages

Programmation Réseau en C : API et Sockets

Ce document présente l'API C pour la programmation réseau, en se concentrant sur la gestion des adresses Internet, des sockets et de l'accès à l'annuaire DNS. Il décrit les structures de données nécessaires, les fonctions associées pour manipuler les adresses IPv4 et IPv6, ainsi que les étapes pour créer et gérer des connexions réseau. Des exemples de code illustrent l'utilisation de ces concepts dans des applications pratiques.

Transféré par

st
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues37 pages

Programmation Réseau en C : API et Sockets

Ce document présente l'API C pour la programmation réseau, en se concentrant sur la gestion des adresses Internet, des sockets et de l'accès à l'annuaire DNS. Il décrit les structures de données nécessaires, les fonctions associées pour manipuler les adresses IPv4 et IPv6, ainsi que les étapes pour créer et gérer des connexions réseau. Des exemples de code illustrent l'utilisation de ces concepts dans des applications pratiques.

Transféré par

st
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Programmation Réseau

API C TCP

[Link]@[Link]
!
UFR Informatique
!
2013-2014
API C

Les flux réseaux en C


• Pré-requis : utiliser correctement les flux
d’entrées-sorties…
• En C, il est important de se rappeler que l’API
est de bas-niveau, i.e. tout est à faire soi-même
ou quasiment…
• retrouver l’adresse à partir du nom
(consultation de l’annuaire)
• forger les bonnes structures (NBO…)
• paramétrer différents mécanismes sous-
jacent (file d’attente…)
• …
2
API C - Les adresses

Les adresses Internet en C


• Rappelons qu’aujourd’hui les adresses Internet peuvent être de
deux types IPv4 ou IPv6, en C cela est représenté par deux
types différents :

• une adresse IPv4 peut être stockée dans une variable de type
struct in_addr ou in_addr_t

• une adresse IPv6 peut être stockée dans une variable de type
struct in6_addr

• La structure interne de ces types peut être oubliée (i.e. aucune


raison d’avoir à manipuler l’intérieur de la structure soi-même)

• Le fichier d’inclusion associé est :

#include <netinet/in.h>
3
API C - Les adresses

• Il existe différentes fonctions de manipulation


de ces structures et permettant de les
convertir depuis ou vers une chaîne de
caractères...

• Inclure le fichier <arpa/inet.h>

• Pour la pile IPv4 :

char *inet_ntoa(struct in_addr);

int inet_aton(const char *,struct in_addr *);

in_addr_t inet_addr(const char *);


convert.c 4
API C - Les adresses

• Pour la pile IPv6, il existe désormais des fonctions


génériques (qui fonctionnent pour toutes les piles) :

const char *inet_ntop(int af, const void *src,


char *chaine,
socklen_t size);
int inet_pton(int af, const char *chaine,
void *dst);

• où af représente la famille protocolaire AF_INET


ou AF_INET6, et src et dst des pointeurs vers des
adresses Internet conformes à la valeur de af

• Ces fonctions ne font PAS appel à l’annuaire, elles


n’effectuent que des transformations entre
représentations!
convert.c 5
API C - Les adresses de socket

Les adresses de socket en C


• On rappelle qu’une socket est un point de
communication et est donc caractérisé par :

• une adresse Internet,

• un numéro de port,

• et un type de communication (le même


pour les deux bouts bien entendu)

6
API C - Les adresses de socket

• la structure sockaddr

• il s’agit d’une structure


générique
représentant l’adresse d’une socket :

#include <sys/socket.h>
struct sockaddr {
socklen_t sa_len;
sa_family_t sa_family;
char sa_data[];
};

7
API C - Les adresses de socket

• Le champ sa_family permet de spécifier le


type de la socket (et donc la structure
implémentant le type sockaddr). POSIX
définit :

• AF_LOCAL ou AF_UNIX, pour une socket de


« locale »

• AF_INET, pour une socket IPv4

• AF_INET6, pour une socket IPv6

• Nous ne nous intéresserons qu’aux sockets du


domaine Internet…
8
API C - Les adresses de socket

• Pour une socket du domaine IPv4, la


structure correspondante est :

#include <netinet/in.h>
struct sockaddr_in {
socklen_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
};

9
API C - Les adresses de socket

• Pour une socket du domaine IPv6, la


structure correspondante est :

#include <netinet/in.h>
struct sockaddr_in6 {
socklen_t sin6_len;
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;

};

10
API C - DNS

L’accès à l’annuaire en C
• On peut avoir besoin de déterminer une
adresse associée à un nom Internet

• Ceci nécessite d’obtenir la résolution du


nom

• Il existe différentes fonctions d’accès à


l’annuaire (DNS)

• l’historique gethostbyname

• la moderne getaddrinfo
11
API C - DNS

• La consultation de l’annuaire peut s’effectuer


simplement par un appel à :

#include <netdb.h>
struct hostent *gethostbyname(const char
*nom);

• où : struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
12
API C - DNS

• les différents champs correspondent à :

• h_name est le nom officiel

• h_aliases est le tableau des surnoms

• h_addrtype est le type de l’adresse (AF_INET en


général ou AF_INET6)

• h_length est la longueur de l’adresse


(sizeof(struct in_addr) ou
sizeof(struct in6_addr))

• h_addr_list est un tableau d’adresses (au format


NBO et terminé par un pointeur NULL)

• Attention la fonction et la structure associée ne


permet pas de choisir quelle résolution (IPv4 ou IPv6)
sera obtenue... On préfèrera getaddrinfo (à venir)
13
API C - DNS

• Un exemple de code permettant d’obtenir


l’ensemble des adresses (IPv4) disponibles pour
chaque nom Internet donné en argument :

• on récupère le hostent associé à un nom

• pour chaque adresse on la convertit pour


affichage à l’aide de la fonction :

char *inet_ntoa(struct sockaddr_in);

qui permet d’obtenir la chaîne de caractères


représentant une adresse IPv4 (avec traduction
NBO si nécessaire)
14
API C - DNS
hbn.c
#include <netdb.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[]) {
struct hostent *he; int i;
if (argc<2) {
fprintf(stderr,"usage: %s name [name...]\n",argv[0]);
exit(EXIT_FAILURE);
}
for (i=1; i<argc; i++) {
char **aliases; struct in_addr **addresses;
printf("--------%s\n",argv[i]);
he = gethostbyname(argv[i]);
if (he==NULL) { printf("unknown\n"); continue; }
printf("Official name : %s\n",he->h_name);
aliases = he->h_aliases;
while ((*aliases)!=NULL) {
printf("Alias : %s\n",*(aliases++));
}
for (addresses=(struct in_addr **)he->h_addr_list;
*addresses!=NULL; addresses++) {
printf("Address : %s\n",inet_ntoa(**addresses));
}
}
return 0;
}

15
API C - DNS

• Un autre exemple plus générique mais donc


complexe consiste à utiliser l’API plus récente :

int getaddrinfo(const char *nom,


const char *service,
const struct addrinfo *paramètres,
struct addrinfo *first);

• que l’on ne décrira que partiellement...

• et qui permet d’obtenir (donc entre autres)


une liste d’adresses (au sens très large)
associées dans l’annuaire à un nom Internet
donné

16
API C - DNS

• La structure addrinfo est :

struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};

17
API C - DNS

• Les champs utiles lors d’une résolution sont :

• ai_family, ai_socktype, ai_protocol

• et pour lesquels une sockaddr est utilisable


(ai_addrlen, ai_addr)

• La résolution fournit une collection


d’informations chaînées via ai_next

• Il est nécessaire de libérer la mémoire de cette


chaîne par appel à :

void freeaddrinfo(struct addrinfo *);


18
API C - DNS
gai.c
int main(int argc,char *argv[]) {
int i;
if (argc<2) { fprintf(stderr,"usage: %s name [name...]\n",argv[0]);
exit(EXIT_FAILURE); }
for (i=1; i<argc; i++) {
struct addrinfo *first_info;
printf("--------%s\n",argv[i]);
if (getaddrinfo(argv[i],NULL,NULL,&first_info)) { printf("unknown\n"); } else {
struct addrinfo *info;
for (info=first_info; info!=NULL; info=info->ai_next) {
char string[MAXLEN]; struct sockaddr *saddr = info->ai_addr;
if (info->ai_socktype==SOCK_DGRAM) continue;
switch (saddr->sa_family) {
case AF_INET:
{ struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
inet_ntop(saddr->sa_family,&(sin->sin_addr),string,MAXLEN); }
break;
case AF_INET6:
{ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
inet_ntop(saddr->sa_family,&(sin6->sin6_addr),string,MAXLEN); }
break;
default:
strcpy(string,"???"); break;
}
printf("Address: %s\n",string);
}
freeaddrinfo(first_info);
}
}
}

19
API C

[Link] 20
API C

[Link] 21
API C

[Link] 22
API C

• La création d’une socket s’effectue par appel à

#include <sys/socket.h>
int socket(int domaine,int type,int protocole);

• où :

• domaine vaut PF_INET ou PF_INET6 (pour ce qui


nous intéresse)
• type SOCK_STREAM (pour ce qui nous intéresse, mais il
existe SOCK_DGRAM, SOCK_RAW ou SOCK_SEQPACKET)
• protocole spécifie un protocole de la famille donnée
(pour nous ce sera 0)

• Les sockets doivent être associées à un point de


communication

23
API C

• Du côté serveur, il est nécessaire d’associer cette


socket à un port donné sur l’une des (ou toutes
les) adresses de la machine

• cette association s’effectue par appel à

#include <sys/socket.h>
int bind(int socket,
const struct sockaddr *adresse,
socklen_t longueur);

• où longueur est la longueur de la structure


décrivant l’adresse

• en général le bind s’effectue sur INADDR_ANY


24
API C

• une fois associée à un point de communication, il est


nécessaire de faire de cette socket une socket serveur

• en faisant en sorte que le système autorise la


réception de demandes connexions entrantes

• en contrôlant le nombre de demandes en attente


possible

• La fonction à appeler est :

#include <sys/socket.h>
int listen(int socket,int attente);

• en général attente est égal à 0 (ce qui signifie qu’on


laisse le système trouver une valeur appropriée)
25
API C

• l’acceptation d’une demande de connexion


entrante s’effectue par appel à

#include <sys/socket.h>
int accept(int socket,
struct sockaddr *adresse,
socklen_t *longueur);

• qui attend qu’une demande de connexion


entrante arrive si la file d’attente est vide (ce
comportement peut être modifié, O_NONBLOCK)

• qui extrait une demande si la file d’attente n’est


pas vide
26
API C

• la valeur de retour de la fonction accept est

• un descripteur d’entrée/sortie
correspondant à une socket de service

• ce descripteur est manipulable comme


n’importe quel autre (O_NONBLOCK, etc.)

• on peut consulter ses caractéristiques via


getsockname()…

27
API C

• Du côté client, il faut demander


l’établissement d’un connexion à l’aide de la
fonction :

int connect(int socket,


const struct sockaddr *adresse,
socklen_t longueur);

• connexion tentée vers la socket serveur à


l’adresse indiquée

28
API C

• Nous savons que les communications via des


socket en mode flux sont en duplex intégral
(full-duplex), i.e. des informations peuvent
circuler dans les deux sens

• il est possible d’interdire certains opérations


futures par appel à :

#include <sys/socket.h>
int shutdown(int socket,int quoi);

• où quoi peut-être :

• SHUT_RD, SHUT_WR, SHUT_RDWR


29
API C

• Une fois la communication établie les


descripteurs peuvent être utilisés pour lire et
écrire à l’aide des fonctions ordinaires
read() et write()

• la terminaison est normalement obtenue par


close()

30
Exemple
serveur.c
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
!
int main(int argc,char *argv[]) {
int s;
struct sockaddr_in a;
!
s = socket(PF_INET,SOCK_STREAM,0);
if (s==-1) {
fprintf(stderr,"socket problem\n");
exit(EXIT_FAILURE);
}
bzero(&a,sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(61234);
a.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s,(struct sockaddr *)&a,sizeof(a))==-1) {
fprintf(stderr,"bind problem");
close(s);
exit(EXIT_FAILURE);
}
31
Exemple
serveur.c
if (listen(s,0)==-1) {
fprintf(stderr,"listen problem");
close(s);
exit(EXIT_FAILURE);
}
int d, l, i;
struct sockaddr_in c;
char buffer[256];
int lus;
do {
if ((d=accept(s,(struct sockaddr *)&c,&l))==-1) {
fprintf(stderr,"accept problem");
close(s);
exit(EXIT_FAILURE);
}
shutdown(s,SHUT_WR);
while ((lus=read(d,buffer,256))>0) {
for (i=0; i<lus; i++) printf("%2x(%c) ",buffer[i],buffer[i]);
printf("\n");
}
close(d);
} while(lus==0);
!
close(s);
}

32
Exemple
client.c
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
!
int main(int argc,char *argv[]) {
int s, i;
struct sockaddr_in a;
struct hostent *he;
!
if (argc<2) {
fprintf(stderr,"usage: %s host\n",argv[0]);
exit(EXIT_FAILURE);
}
he = gethostbyname(argv[1]);
if (he==NULL) {
fprintf(stderr,"problem with name resolution\n"),
exit(EXIT_FAILURE);
}
s = socket(PF_INET,SOCK_STREAM,0);
if (s==-1) {
fprintf(stderr,"socket problem\n");
exit(EXIT_FAILURE);
}

33
Exemple
client.c
bzero(&a,sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(61234);
memcpy(&(a.sin_addr.s_addr),
he->h_addr_list[0],he->h_length);
a.sin_addr.s_addr = inet_addr("[Link]");
if (connect(s,(struct sockaddr *)&a,
sizeof(a))==-1) {
fprintf(stderr,"bind problem");
close(s);
exit(EXIT_FAILURE);
}
char *n = "bonjour";
printf("sent %d\n",write(s,n,strlen(n)));
close(s);
exit(EXIT_SUCCESS);
}

34
API C

• Pour les sockets, il est possible de lire via :

ssize_t recv(int socket,void *tampon,


size_t longueur,int options);

• où les options possibles sont des


combinaisons de :

• MSG_PEEK, pour lire sans extraire les données

• MSG_OOB, pour lire les messages hors-bande

• MSG_WAITALL, pour demander une lecture


de la quantité donnée (attente si nécessaire)
35
API C

• Pour les sockets, il est possible d’écrire via :

ssize_t send(int socket,const void *tampon,


size_t longueur,int options);

• où options est une combinaison de :

• MSG_EOR, pour terminer l’envoi par une


marque de fin de message

• MSG_OOB, pour envoyer des données hors-


bande

36
API C

• On notera qu’il est aussi possible de réaliser


des transmissions via recvfrom() et
sendto()

• mais on réservera ces fonctions pour des


communications en mode par paquet

• ou encore via recvmsg() et sendmsg()

• on réservera l’emploi de celles-ci pour des


écritures/lectures de messages

37

Vous aimerez peut-être aussi