Sockets BSD/Windows






1) Présentation

Un socket est un point de communication. Il permet d'envoyer et de recevoir des paquets de données. Un socket est créé avec la fonction socket(). connect() connecte un socket à l'adresse d'un socket distant. Cette fonction est spécifique au client. bind() lie un socket à une adresse locale, ce qui permet ensuite d' écouter avec listen(), pour pouvoir accepter les nouvelles connections avec accept(). Ces fonctions sont spécifiques au serveur. send() envoie des données, et recv() reçoit ces données depuis un socket. getsockname() renvoie l'adresse locale d'un socket et getpeername() renvoie l'adresse d'un socket distant. getsockopt(2) et setsockopt(2) servent à fixer ou à lire des options du niveau socket ou protocole. ioctl(2) peut servir pour lire ou écrire d'autres options. close() sert à fermer un socket. shutdown() ferme un sens de communication d'un socket full-duplex connecté. Au moment d' accepter les connections, le serveur peut appliquer deux méthodes différentes: select() implémente le multiplexage d'entrées/sorties synchrone, alors que fork() crée un nouveau process pour le nouveau client.

Domaines de communication

AF_INET AF_UNIX

Types de communication

TCP: SOCK_STREAM Le domaine de communication TCP fonctionne en mode connecté: la transmission est établie et fiabilisée par un système sous-jacent de contrôle par envoi avec accusé de réception. UDP: SOCK_DGRAM Le mode UDP travaille en mode non connecté: la fiabilité de la connection n'est pas garantie, on n'a aucune certitude que le paquet envoyé arrive bien à l' autre bout. En effet le protocole TCP se base sur un système d'envoi avec accusé de réception de bas niveau pour maintenir et garantir l' intégrité de la connection. En UDP, ces traitements n' existent pas! Celà diminue la fiabilité mais rendent les traitements plus rapides. Ce protocole est donc préféré pour le streaming vidéo -où ce n'est pas grave si une image n'arrive pas- il est aussi utilisé par le protocole DNS, et bien d' autres. Créer le socket bind() sendto() et recvfrom() close() SOCK_RAW Sockets de bas niveau. En-têtes IP, TCP, UDP

Structures

struct sockaddr_in En AF_INET seulement (ipv4) struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ u_int16_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; struct in_addr /* Internet address. */ struct in_addr { u_int32_t s_addr; /* address in network byte order */ }; struct hostent La structure hostent est définie ainsi 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] /* pour rétro-compatibilité. */ Les membres de la structure hostent sont : h_name Nom officiel de l'hôte. h_aliases Une table, terminée par zéro, d'alternatives au nom officiel de l'hôte. h_addrtype Le type d'adresse (en fait, toujours AF_INET). h_length La longueur, en octets, de l'adresse. h_addr_list Une table, terminée par zéro, 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 compatibilite ascendante. struct sockaddr_un En UDP seulement. struct sockaddr_un { uint8_t sun_len; sa_family sun_family /*AF_LOCAL*/ char sun_path[104] /* null-terminated pathname*/ }

2) Fonctions

Les fonctions détaillées ci-dessous proviennent essentiellement des pages de manuel du programmeur Linux, traduites par Christophe Blaess. J'ai corrigé quelques (rares) fautes d' orthographe, et par endroits ajouté quelques parties originales.

socket()

Créer un point de communication. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int socket(int domain, int type, int protocol);
DESCRIPTION socket crée un point de communication, et renvoie un descripteur. Domaine: Le paramètre domain indique le domaine de communication pour le dialogue ; ceci sélectionne la famille de protocole à employer. Elles sont définies dans le fichier <linux/socket.h>. Les formats actuellement proposés sont : nom Utilisation Page ------------------------------------------------------------------------ PF_UNIX,PF_LOCAL Communication locale unix(7) PF_INET IPv4 Protocoles Internet ip(7) PF_INET6 IPv6 Protocoles Internet PF_IPX IPX - Protocoles Novell PF_NETLINK Interface utilisateur noyau netlink(7) PF_X25 Protocole ITU-T X.25 / ISO-8208 x25(7) PF_AX25 Protocole AX.25 radio amateur PF_ATMPVC Accès direct ATM PVCs PF_APPLETALK Appletalk ddp(7) PF_PACKET Interface paquet bas-niveau packet(7) Les sockets ont le type, indiqué, ce qui fixe la sémantique des communications. Les types définis actuellement sont : Type: SOCK_STREAM Support de dialogue garantissant l'intégrite, fournissant un flux de données binaires, et intégrant un mécanisme pour les transmissions de données hors-bande. SOCK_DGRAM Transmissions sans connection, non garantie, de datagrammes de longueur fixe, généralement courte. SOCK_SEQPACKET Dialogue garantissant l'intégrite, pour le transport de datagrammes de longueur fixe. Le lecteur peut avoir à lire le paquet de données complet à chaque appel système read. SOCK_RAW Accès direct aux données réseau. SOCK_RDM Transmission fiable de datagrammes, sans garantie de l'ordre de délivrance. SOCK_PACKET Obsolète, à ne pas utiliser dans les programmes actuels. Voir packet(7). Certains types de sockets peuvent ne pas être implémentés par toutes les familles de protocoles. Par exemple, SOCK_SEQPACKET n'est pas implémenté pour AF_INET. Protocole: Le protocole à utiliser sur le socket est indiqué par l'argument protocol. Normalement il n'y a qu'un seul protocole par type de socket pour une famille donnée, auquel cas l'argument protocol peut être nul. Néanmoins rien ne s'oppose à ce que plusieurs protocoles existent, auquel cas il est nécessaire de le spécifier. Le numéro de protocole dépend du domaine de communication de le socket. Voir protocols(5). Voir getprotoent(3) pour savoir comment associer un nom de protocole à un numéro. Un socket de type SOCK_STREAM est un flux d'octets full-duplex, similaire aux tubes (pipes). Elle ne préserve pas les limites d'enregistrements. Une socket SOCK_STREAM doit être dans un état connecté avant que des données puisse y être lues ou écrites. Une connection sur une autre socket est établie par l'appel système connect(). Une fois connectée les données y sont transmises par read(2) et write(2) ou par des variantes de send() et recv(). Quand une session se termine, on referme le socket avec close(). Les données hors-bande sont envoyées ou reçues en utilisant send() et recv(). Les protocoles de communication qui implémentent les sockets SOCK_STREAM garantissent qu'aucune donnée n'est perdue ou dupliquée. Si un bloc de données, pour lequel le correspondant a suffisament de place dans son buffer, n'est pas transmis correctement dans un délai raisonnable, la connection est considérée comme inutilisable. Si l'option SO_KEEPALIVE est activée sur le socket, le protocole vérifie, d'une manière qui lui est spécifique, si le correspondant est toujours actif. Un signal SIGPIPE est envoyé au processus tentant d'écrire sur une socket inutilisable, forçant les programmes ne gérant pas ce signal à se terminer. Les sockets de type SOCK_SEQPACKET emploient les mêmes appels systèmes que celles de types SOCK_STREAM, à la différence que la fonction read(2) ne renverra que le nombre d'octets requis, et toute autre donnée restante sera éliminée. De plus les frontières des messages seront préservées. Les sockets de type SOCK_DGRAM ou SOCK_RAW permettent l'envoi d datagrammes aux correspondants indiqués dans l'appel système send(). Les datagrammes sont généralement lus par la fonction recvfrom(2), qui fournit également l'adresse du correspondant. Les sockets SOCK_PACKET sont obsolètes. Elles servent à recevoir les paquets bruts directement depuis le gestionnaire de périphérique. Utilisez plutôt packet(7). Un appel à fcntl(2) avec l'argument F_SETOWN permet de préciser un groupe de processus qui recevront un signal SIGURG lors de l'arrivée de données hors-bande, ou le signal SIGPIPE lorsqu'une connection sur un socket SOCK_STREAM se termine inopinément. Cette fonction permet également de valider des entrées/sorties non bloquantes, et une notification asynchrone des évènements par le signal SIGIO. L'utilisation de F_SETOWN est équivalent à un appel ioctl(2) avec l'argument FSIOSETOWN ou SIOCSPRGR. Lorsque le réseau indique une condition d'erreur au module du protocole (par exemple l'utilisation d'un message ICMP au lieu d'IP), un drapeau signale une erreur en attente sur le socket. L'opération suivante sur cette socket renverra ce code d'erreur. Pour certains protocoles, il est possible d'activer une file d'attente d'erreurs par socket. Pour plus de détails, voir IP_RECVERR dans ip(7). Les opérations sur les sockets sont représentées par des options du niveau socket. Ces options sont définies dans sys/socket.h. Les fonctions setsockopt(2) et getsockopt(2) sont utilisées respectivement pour fixer ou lire les options. VALEUR RENVOYÉE socket retourne un descripteur référençant le socket créée en cas de réussite. En cas d'échec -1 est renvoyé, et errno contient le code d'erreur. ERREURS EPROTONOSUPPORT Le type de protocole, ou le protocole lui-même n'est pas disponible dans ce domaine de communication. EAFNOSUPPORT L'implémentation ne supporte pas le famille d'adresses indiquée. ENFILE La table des descripteurs par processus est pleine. EMFILE La table des fichiers est pleine. EACCES La création d'une telle socket n'est pas autorisée. ENOBUFS ou EnomEM Pas suffisament d'espace pour allouer les buffers nécessaires. EINVAL Protocole inconnu, ou famille de protocole inexistante. D'autres erreurs peuvent être dues aux modules de protocoles sous-jacents. CONFORMITÉ BSD 4.4 (la fonction socket est apparue dans BSD 4.2). Relativement portable vers les systèmes non-BSD supportant des clones des sockets BSD (y compris les variantes de System V). NOTE Les constantes explicites utilisées sous BSD 4.* pour les familles de de protocoles sont PF_UNIX, PF_INET... et AF_UNIX... sont utilisées pour les familles d'adresses. Toutefois même la page de manuel de BSD indiquait "La famille de protocoles est généralement la même que la famille d'adresse", et les standards ultérieurs utilisent AF_* partout. BOGUES SOCK_UUCP n'est pas encore implémentée. VOIR AUSSI accept(), bind(), connect(), fcntl(2), getpeername(), getsockname(), getsockopt(2), ioctl(2), listen(), read(2), recv(), select(), send(), shutdown(), socketpair(2), write(2), getprotoent(3), ip(7), socket(), tcp(7), udp(7), unix(7) TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

connect()

Débuter une connection sur un socket. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
DESCRIPTION Le paramètre sockfd est un socket. Si le socket est du type SOCK_DGRAM, alors serv_adr est l'adresse à laquelle les datagrammes seront envoyés par défaut, et la seule adresse depuis laquelle ils seront reçus. Si le socket est du type SOCK_STREAM ou SOCK_SEQPACKET, cette fonction tente de se connecter à un autre socket. L'adresse de l'autre socket est indiquée par serv_addr, qui doit être une adresse (de longueur addrlen) dans le même domaine que le socket. Chaque domaine de communication interprète le paramètre serv_addr, à sa manière. En général, les sockets des protocoles orientés connection ne réussissent un appel connect qu'une seule fois, alors qu'une socket d'un protocole sans connection peut appeler connect plusieurs fois pour changer son affectation. Une socket sans connection peut interrompre son affectation en se connectant sur une adresse avec le membre sa_family de la structure sockaddr à la valeur AF_UNSPEC. VALEUR RENVOYÉE connect renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur. ERREURS Voici une liste d'erreurs générales concernant les sockets, il peut en exister d'autres spécifiques au domaine employé. EBADF Mauvais descripteur. EFAULT La structure d'adresse pointe en dehors de l'espace d'adressage. ENOTSOCK Le descripteur ne correspond pas à une socket. EISCONN le socket est déjà connectée. ECONNREFUSED La connection est refusée par le serveur. ETIMEDOUT Dépassement du délai maximum pendant la connection. Le serveur peut être trop chargé pour accepter une nouvelle connection. Remarquez que pour les sockets IP, le délai peut être très long si les syncookies sont activés sur le serveur. ENETUNREACH Le réseau est inaccessible. EADDRINUSE L'adresse est déjà utilisée. EINPROGRESS Le socket est non-bloquant, et la connection ne peut pas être établie immédiatement. Il est alors possible d'utiliser select() ou poll(2) pour attendre que le socket soit disponible en écriture. Une fois que select confirme la possibilité d'écrire, utilisez getsockopt(2) pour lire l'option SO_ERROR du niveau SOL_SOCKET et déterminer si connect s'est terminé avec succès (SO_ERROR vaut zéro) ou en échec (SO_ERROR contient l'un des codes d'erreurs listés ici, indiquant le problème). EALREADY L'adresse est déjà en cours d' utilisation. Le socket est non-bloquant et une tentative de connection précédente ne s'est pas encore terminée. EAGAIN Pas de port local disponible, ou pas assez de place dans les tables de routage. Pour PF_INET voir l'appel sysctl net.ipv4.ip_local_port_range dans ip(7) pour savoir comment augmenter le nombre de ports locaux. EAFNOSUPPORT L'adresse transmise n'appartient pas à la famille indiquée dans son champ sa_family. EACCES, EPERM L'utilisateur a tenté de connecter une adresse broadcast sans avoir activé l'attribut broadcast, ou la demande de connection a échoué à cause des règles d'un firewall local. CONFORMITÉ SVr4, 4.4BSD (La fonction connect est apparue en premier dans BSD 4.2). SVr4 décrit des erreurs générales EADDRNOTAVAIL, EINVAL, EAFNOSUPPORT, EALREADY, EINTR, EPROTOTYPE, et ENOSR. Il décrit également plusieurs causes d'erreurs non présentées ici. NOTE Le troisième argument de connect est en fait un int (et c'est ce qu' utilisent BSD 4.*, libc4 et libc5). Une certaine confusion POSIX résulte du "socklen_t" actuel. Les propositions de standard n'ont pas encore été adoptées, mais glibc2 les suit déjà et utilise socklen_t. Pour plus de détails voir accept(). BOGUES La déconnection d'un socket en appelant connect avec un adresse de type AF_UNSPEC n'est pas encore implémentée. VOIR AUSSI accept(), bind(), listen(), socket(), getsockname() TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot, 2006

bind()

Lier un socket à une adresse locale. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
DESCRIPTION bind fournit au socket sockfd l'adresse locale my_addr. my_addr est longue de addrlen octets. Traditionnellement cette opération est appelée "affectation d'un nom à un socket" (Quand un socket est créé avec l'appel système socket(), il existe dans l'espace des noms mais n'a pas de nom assigné). Il est normalement nécessaire d'affecter une adresse locale avec bind avant qu'un socket SOCK_STREAM puisse recevoir des connections (voir accept()). Les règles d'affectation de nom varient suivant le domaine de communication. Consultez le manuel Linux section 7 pour de plus amples informations. Pour AF_INET voir ip(7), pour AF_UNIX voir unix(7), pour AF_APPLETALK voir ddp(7), pour AF_PACKET voir packet(7), pour AF_X25 voir x25(7) et pour AF_NETLINK voir netlink(7). VALEUR RENVOYÉE L'appel renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur. ERREURS EBADF sockfd n'est pas un descripteur valide. EINVAL Le socket possède déjà une adresse. Ceci peut changer dans l'avenir : voir linux/unix/sock.c pour les détails. EACCES L'adresse est protégée et l'utilisateur n'est pas le Super-User. ENOTSOCK L'argument est un descripteur de fichier, pas une socket. Les erreurs suivantes sont spécifiques aux sockets du domaine UNIX (AF_UNIX) : EINVAL La longueur addr_len est fausse, ou le socket n'est pas de la famille AF_UNIX. EROFS L'i-noeud se trouverait dans un système de fichiers en lecture seule. EFAULT my_addr pointe en dehors de l'espace d'adresse accessible. ENAMETOOLONG my_addr est trop long ENOENT Le fichier n'existe pas. ENOMEM pas assez de mémoire pour le noyau. ENOTDIR Un composant du chemin d'accès n'est pas un répertoire. EACCES L'accès à un composant du chemin d'accès n'est pas autorisé. ELOOP my_addr contient des références circulaires (à travers un lien symbolique). BOGUES Les options de proxy transparent ne sont pas décrites. CONFORMITÉ SVr4, BSD 4.4 (l'appel système bind est apparu dans BSD 4.2). SVr4 indique des erreurs générales supplémentaires EADDRNOTAVAIL, EADDRINUSE, et ENOSR, ainsi que les conditions d'erreurs spécifiques au domaine Unix EIO et EISDIR. NOTE Le troisième argument de bind est en fait un int (et c'est ce qu' utilisent BSD 4.*, libc4 et libc5). Une certaine confusion POSIX résulte du "socklen_t" actuel. Voir accept(). VOIR AUSSI accept(), connect(), listen(), socket(), getsockname(), ip(7). TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot, 2006

listen()

Attendre des connections sur un socket. SYNOPSIS
#include <sys/socket.h>
int listen(int s, int backlog);
DESCRIPTION Pour accepter des connections, un socket est d'abord créé avec socket(), puis le désir d'accepter des connections entrantes, et la limite de la file d'entrée sont indiqués avec listen, ensuite les connections seront acceptées avec accept(). L'appel système listen s'applique seulement aux sockets de type SOCK_STREAM ou SOCK_SEQPACKET. Le paramètre backlog définit une longueur maximale pour la file des connections en attente. Si une nouvelle connection arrive alors que la file est pleine, le client reçoit une erreur indiquant ECONNREFUSED, ou, si le protocole sous-jacent supporte les retransmissions, la requête peut être ignorée afin qu'un nouvel essai réussisse. Le comportement de backlog a été modifié sur les sockets TCP dans Linux 2.2. Il s'agit à présent de la longueur de la file d'attente pour les socket totalement établies en attente d'acceptation, plutôt que les requêtes de connection incomplètes. La longueur maximale de la file d'attente des connections incomplètes peut être configurée avec l'appel sysctl tcp_max_syn_backlog. Lorsque les syncookies sont activés, il n'y a pas de longueur maximale et la configuration sysctl est ignorée. Voir tcp(7) pour plus de détail. VALEUR RENVOYÉE listen renvoie 0 si il réussit, ou -1 en cas d'échec, auquel cas errno contient le code d'erreur. ERREURS EADDRINUSE Une autre socket est déjà à l'écoute sur le même port. EBADF s n'est pas un descripteur valide ENOTSOCK L'argument s n'est pas un socket. EOPNOTSUPP Le type de socket ne supporte pas l'appel système listen. CONFORMITÉ SVr4, BSD (l'appel système listen est apparu dans BSD 4.2). BOGUES Si le socket est de type AF_INET (internet), et si l'argument backlog est supérieur à la constante SOMAXCONN (128 dans Linux 2.0 et 2.2), il est silencieusement ramené à SO_MAXCONN. Pour la portabilité des applications, ne vous fiez pas à cette valeur puisque BSD (et d'autres dérivés) limitent l'argument backlog à 5. VOIR AUSSI accept(), connect(), socket() TRADUCTION Christophe Blaess, 1996-2003.

accept()

Accepter une connection sur un socket. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int accept(int sock, struct sockaddr *adresse, socklen_t longueur);
DESCRIPTION accept est utilisé généralement avec des processus serveurs orientés connection. Cet appel système est employé avec les sockets utilisant un protocole en mode connecté (SOCK_STREAM, SOCK_SEQPACKET et SOCK_RDM). Il extrait la première connection de la file des connections en attente, crée un nouveau socket avec essentiellement les mêmes propriétés que sock et alloue pour ce socket un nouveau descripteur de fichier qu'il renvoie. Le nouveau socket n'est pas en état d'écoute. Le socket original sock n'est pas modifiée par l'appel-système. Remarquez que les attributs du descripteur de fichier (tout ce qu'on peut configurer avec l'option F_SETFL de fcntl() comme l'état non-bloquant ou asynchrone), ne sont pas hérités durant un accept. L'argument sock est un socket qui a été créé avec la fonction socket() attaché à une adresse avec bind() et attend des connections après un appel listen(). L'argument adresse est un pointeur sur une structure sockaddr. La structure sera remplie avec l'adresse du correspondant se connectant, telle qu'elle est connue par la couche de communication. Le format exact du paramètre adresse dépend du domaine dans lequel la communication s'établit. (Voir socket() et la page de manuel correspondant au protocole). L'argument longueur est un paramètre-résultat : il doit contenir initialement la taille de la structure pointée par adresse, et est renseigné au retour par la longueur réelle (en octet) de l'adresse remplie. Quand adresse vaut NULL, rien n'est rempli. S'il n'y a pas de connection en attente dans la file, et si le socket n'est pas marqué comme non-bloquant, accept se met en attente d'une connection. Si le socket est non-bloquant, et qu'aucune connection n'est présente dans la file, accept retourne une erreur EAGAIN. Pour être prévenu de l'arrivée d'une connection sur une socket on peut utiliser select() ou poll(2). Un évènement "lecture" sera délivré lorsqu'une tentative de connection aura lieu, et on pourra alors appeler accept pour la valider. Autrement, on peut configurer le socket pour qu'elle envoie un signal SIGIO lorsqu'une activité la concernant se produit, voir socket() pour plus de détails. Pour certains protocoles nécessitant une confirmation explicite, comme DECNet, accept peut être considéré comme extrayant simplement la connection suivante de la file, sans demander de confirmation. On peut effectuer la confirmation par une simple lecture ou écriture sur le nouveau descripteur, et le rejet en fermant le nouveau socket. Pour le moment, seul DECNet se comporte ainsi sous Linux. NOTES Il n'y a pas nécessairement de connection en attente après la réception de SIGIO ou après que select() ou poll(2) indiquent quelque chose à lire. En effet la connection peut avoir été annulée à cause d'une erreur réseau asynchrone ou par un autre thread avant que accept ne se termine. Si cela se produit, l'appel bloquera en attendant une autre connection. Pour s'assurer que accept ne bloquera jamais, le socket sock transmise doit avoir l'attribut O_NONBLOCK (voir socket()). VALEUR RENVOYÉE L'appel renvoie -1 en cas d'erreur. S'il réussit il renvoie un entier non-négatif, constituant un descripteur pour la nouvelle socket. GESTION DES ERREURS Sous Linux, accept renvoie les erreurs réseau déjà en attente sur le socket comme une erreur de l'appel-système. Ce comportement diffère d'autres implémentations des sockets BSD. Pour un comportement fiable, une application doit détecter les erreurs réseau définies par le protocole après le accept et les traiter comme des erreurs EAGAIN, en réitérant le mécanisme. Dans le cas de TCP/IP, ces erreurs sont ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, et ENETUNREACH. ERREURS accept doit échouer si : EAGAIN ou EWOULDBLOCK le socket est non-bloquant et aucune connection n'est présente dans la file d'attente. EBADF Le descripteur est invalide. ENOTSOCK Le descripteur n'est pas celui d'un socket. EOPNOTSUPP La socket de référence n'est pas de type SOCK_STREAM. EINTR L'appel-système a été interrompu par l'arrivée d'un signal avant qu'une connection valide ne survienne. ECONNABORTED Une connection a été abandonnée. EINVAL La socket n'est pas en attente de connections. EMFILE La limite des descripteurs ouverts pour le processus a été atteinte. ENFILE Le nombre maximal de descripteurs sur le système a été atteint. accept peut échouer si : EFAULT adresse n'est pas dans l'espace d'adressage accessible en écriture. ENOBUFS, EnomEM Pas assez de mémoire disponible. En général, cette erreur due à la taille limitée du buffer des sockets, et pas à la mémoire système proprement dite. EPROTO Erreur de protocole. La version Linux de accept peut échouer si : EPERM Les règles du firewall interdisent la connection. De plus il peut se produire des erreurs réseau dépendant du protocole du socket. Certains noyaux Linux peuvent renvoyer d'autres erreurs comme ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT, ETIMEDOUT. L'erreur ERESTARTSYS peut être rencontrée durant un suivi dans un débogueur. CONFORMITÉ SVr4, BSD 4.4 (La fonction accept est apparue dans BSD 4.2). La page de manuel BSD documente cinq erreurs possibles (EBADF, ENOTSOCK, EOPNOTSUPP, EWOULDBLOCK, EFAULT). SUSv3 documente les erreurs EAGAIN, EBADF, ECONNABORTED, EINTR, EINVAL, EMFILE, ENFILE, ENOBUFS, EnomEM, ENOTSOCK, EOPNOTSUPP, EPROTO, EWOULDBLOCK, De Plus, SUSv2 documentait EFAULT et ENOSR. La version Linux de accept ne fait pas hériter les attributs comme O_NONBLOCK. Ce comportement est différent d'autres implémentations BSD. Les programmes portables ne doivent pas s'appuyer sur cette particularité, et doivent reconfigurer les attributs sur la socket renvoyée par accept. NOTE Le troisième argument de accept était, à l'origine, déclaré comme un 'int *' (ceci dans libc4 et libc5 ainsi que pour beaucoup d'autres systèmes comme BSD 4.*, SunOS 4, SGI). Une proposition de standard POSIX 1003.1g l'a modifié en 'size_t *' et c'est ce qu'utilise SunOS. Les dernières propositions POSIX en ont fait un 'socklen_t *', ce que suivent les spécifications Single Unix, et la glibc2. Pour citer Linus Torvalds: "Toute bibliothèque sensée doit garder "socklen_t" équivalent à un int. Toute autre chose invaliderait tout le niveau des sockets BSD. POSIX l'avait d'abord remplacé par un size_t, et je m'en suis plaint violemment (ainsi que d'autres heureusement, mais bien entendu pas tant que ça). Le remplacement par un size_t est complètement inutile car size_t à exactement la même taille qu'un int sur les architectures 64 bits par exemple. Et il a la même taille qu'un "int" parce que c'était l'interface des sockets BSD. Quoiqu'il en soit, les gens de POSIX ont compris et ont créé un "socklen_t". Ils n'auraient jamais dû y toucher, mais une fois commencé, ils ont décidé de créer un type spécifique, pour des raisons inavouées (probablement quelqu'un qui ne veut pas perdre la face en expliquant que le premier travail était stupide et ils ont simplement renommé leur bricolage). VOIR AUSSI bind(), connect(), listen(), select(), socket() TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

send() sendto() sendmsg()

send, sendto, sendmsg - Envoyer un message sur une socket. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int send(int s, const void *msg, size_t len, int flags);
int sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
int sendmsg(int s, const struct msghdr *msg, int flags);
DESCRIPTION Send, sendto, et sendmsg permettent de transmettre un message à destination d'un autre socket. Send ne peut être utilisé qu'avec les sockets connectés alors que sendto et sendmsg peuvent être utilisés tout le temps. L'adresse de la cible est donnée par to avec la longueur tolen. La longueur du message est indiquée dans len. Si le message est trop long pour être transmis intégralement au protocole sous-jacent, l'erreur EMSGSIZE sera déclenchée et rien ne sera émis. Aucune indication d'échec de distribution n'est fournie par send. Seules les erreurs locales sont détectées, et indiquées par une valeur de retour -1. Si le socket ne dispose pas de la place suffisante pour le message, alors send va bloquer, à moins que le socket ait été configuré en mode d'entrées/sorties non-bloquants auquel cas elle renverra EAGAIN. On peut utiliser l'appel système select() pour vérifier s'il est possible d'émettre des données. Le paramètre flags peut contenir une ou plusieurs des options suivantes: MSG_OOB est utilisée pour émettre des données hors-bande sur un socket qui l'autorise (par ex : SOCK_STREAM). Le protocole sous-jacent doit également autoriser l'émission de données hors-bande. MSG_DONTROUTE est utilisé pour empêcher la transmission d'un paquet vers une passerelle, n'envoyer de données que vers les hôtes directement connectés au réseau. Ceci n'est normalement employé que par les programmes de diagnostic ou de routage. Cette option n'est définie que pour les familles de protocoles employant le routage, pas les sockets par paquets. MSG_DONTWAIT active le mode non-bloquant. Une opération qui devrait bloquer renverra EAGAIN à la place (Cela peut être également paramétré avec l'option O_NONBLOCK de la fonction F_SETFL de fcntl(2)). MSG_NOSIGNAL demande de ne pas envoyer de signal SIGPIPE d'erreur sur les sockets connectés lorsque le correspondant coupe la connection. L'erreur EPIPE est toutefois renvoyée. MSG_CONFIRM (Depuis Linux 2.3) Indiquer à la couche de liaison qu'une réponse correcte a été reçue du correspondant. Si la couche de liaison n'a pas cette confirmation, elle va ré-interroger régulièrement le voisinage (par exemple avec un ARP unicast). Seulement valide pour les sockets SOCK_DGRAM et SOCK_RAW et uniquement implémenté pour IPv4 et IPv6. Voir arp(7) pour plus de détails. La définition de la structure msghdr se trouve ci-dessous. Voir recv() pour une description exacte de ses champs. struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ socklen_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ }; On peut transmettre des informations de service en employant les membres msg_control et msg_controllen. La longueur maximale du buffer de service que le noyau peut gérer est limité par socket par la valeur net.core.optmem_max de sysctl(). Voir socket(). VALEUR RENVOYÉE Ces appels systèmes renvoient le nombre de caractères émis, ou -1 s'ils échouent, auquel cas errno contient le code d'erreur. ERREURS Voici les erreurs standards engendrés par la couche socket. Des erreurs supplémentaires peuvent être déclenchées par les protocoles sous-jacents. Voir leurs pages de manuel respectives. EBADF Descripteur de socket invalide. ENOTSOCK L'argument s n'est pas une socket. EFAULT Un paramètre pointe en dehors de l'espace d'adressage accessible. EMSGSIZE La socket nécessite une émission intégrale du message mais la taille de celui-ci ne le permet pas. EAGAIN ou EWOULDBLOCK La socket est non-bloquante et l'opération demandée bloquerait. ENOBUFS La file d'émission de l'interface réseau est pleine. Ceci indique généralement une panne de l'interface réseau, mais peut également être dû à un engorgement passager. Ceci ne doit pas se produire sous Linux, les paquets sont silencieusement éliminés. EINTR Un signal a été reçu. ENOMEM Pas assez de mémoire pour le noyau. EINVAL Un argument invalide a été transmis. EPIPE L'écriture est impossible (correspondant absent). Dans ce cas le processus recevra également un signal SIGPIPE sauf s'il a activée l'option MSG_NOSIGNAL. CONFORMITÉ BSD 4.4 (cet appel système est apparu dans BSD 4.2), SVr4, Draft POSIX 1003.1g. MSG_CONFIRM est une extension Linux. NOTE Les prototypes fournis plus haut suivent les Spécifications Single Unix, tout comme glibc2. L'argument flags était un `int' dans BSD 4.*, mais 'unsigned int' dans libc4 et libc5. L'argument len était un 'int' dans BSD 4.* et libc4, mais un `size_t' dans libc5; L'argument tolen était un 'int' dans BSD 4.*, libc4 et libc5. Voir aussi les notes accompagnant la page accept(). VOIR AUSSI fcntl(2), recv(), select(), getsockopt(2), sendfile(2), socket(), write(2), ip(7), tcp(7), udp(7) TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

recv() recvfrom() recvmsg()

recv, recvfrom, recvmsg - Recevoir un message sur une socket. SYNOPSIS
#include <sys/types.h> #include <sys/socket.h>
int recv(int s, void *buf, int len, unsigned int flags);
int recvfrom(int s, void *buf, int len, unsigned int flags struct sockaddr *from, socklen_t *fromlen);
int recvmsg(int s, struct msghdr *msg, unsigned int flags);
DESCRIPTION Les appels système recvfrom et recvmsg sont utilisés pour recevoir des messages depuis un socket s, et peuvent servir sur un socket orienté connection ou non. Si from n'est pas NULL, et si le socket n'est pas orientée connection, l'adresse de la source du message y est insérée. L'argument fromlen est un paramètre résultat, initialisé à la taille du buffer from, et modifié en retour pour indiquer la taille réelle de l'adresse enregistrée. L'appel recv est normalement utilisé sur un socket connecté (voir connect()) et est équivalent à recvfrom avec un paramètre from nul. Ces trois routines renvoient la longueur du message si elles réussissent. Si un message est trop long pour tenir dans le buffer, les octets supplémentaires peuvent être abandonnés suivant le type de socket utilisé (voir socket()). Si aucun message n'est disponible sur la socket, les fonctions de réception se mettent en attente, à moins que le socket soit non bloquant (voir fcntl(2)) auquel cas la valeur -1 est renvoyée, et errno est positionnée à EAGAIN. Les fonctions de réception renvoient normalement les données disponibles sans attendre d'avoir reçu le nombre exact réclamé. Les appels système select() ou poll(2) peuvent permettre de déterminer si des données supplémentaires sont disponibles. L'argument flags de l'appel recv est constitué par un OU binaire entre une ou plusieurs des valeurs suivantes : MSG_OOB permet la lecture des données hors-bande qui ne seraient autrement pas placées dans le flux de données normales. Certains protocoles placent ces données hors-bande en tête de la file normale, et cette option n'a pas lieu d'être dans ce cas. MSG_PEEK permet de lire les données en attente dans la file sans les enlever de cette file. Ainsi une lecture ultérieure renverra à nouveau les mêmes données. MSG_WAITALL demande que l'opération de lecture soit bloquée jusqu'à ce que la requête complète soit satisfaite. Toutefois la lecture peut renvoyer quand même moins de données que prévu si un signal est reçu, ou si une erreur ou une déconnection se produisent. MSG_NOSIGNAL désactive l'émission de SIGPIPE sur les sockets connectés dont le correspondant disparaît. MSG_TRUNC Renvoyer la longueur réelle du paquet, même s'il était plus long que le buffer transmis. Valide uniquement pour les sockets paquets. MSG_ERRQUEUE Cet attribut demande que les erreurs soient reçues depuis la file d'erreur du socket. Les erreurs sont transmises dans un message annexe dont le type dépend du protocole (IP_RECVERR pour IPv4). Il faut alors fournir un buffer de taille suffisante. Voir cmsg(3) et ip(7) pour plus de détails. Le contenu du paquet original qui a causé l'erreur est passé en tant que données normales dans msg_iovec. L'adresse de destination originale du datagramme ayant causé l'erreur est fournie dans msg_name. Pour les erreurs locales, aucune adresse n'est passée (ceci peut être vérifié dans le membre cmsg_len de cmsghdr). Pour les erreurs reçues, le MSG_ERRQUEUE est placé dans msghdr. Après qu'une erreur ait été transmise, l'erreur en attente sur le socket est régénérée en fonction de la prochaine erreur dans la file, et sera transmise lors de l'opération suivante sur le socket. L'erreur est contenue dans une structure sock_extended_err : #define SO_EE_ORIGIN_NONE 0 #define SO_EE_ORIGIN_LOCAL 1 #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 struct sock_extended_err { u_int32_t ee_errno; /* numéro d'erreur */ u_int8_t ee_origin; /* origine de l'erreur */ u_int8_t ee_type; /* type */ u_int8_t ee_code; /* code */ u_int8_t ee_pad; /* remplissage */ u_int32_t ee_info; /* données supplémentaires*/ u_int32_t ee_data; /* autres données */ /* More data may follow */ }; struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *); ee_errno contient le code errno de l'erreur en file. ee_origin est le code d'origine de l'erreur. Les autres champs sont spécifiques au protocole. La macro SOCK_EE_OFFENDER renvoie un pointeur sur l'adresse de l'objet réseau ayant déclenché l'erreur, à partir d'un pointeur sur le message. Si l'adresse n'est pas connue, le membre sa_family de la structure sockaddr contient AF_UNSPEC et les autres champs de la structure sockaddr sont indéfinis. Le contenu du paquet ayant déclenché l'erreur est transmis en données normales. Pour les erreurs locales, aucune adresse n'est transmise (ceci peut être vérifié dans le champ cmsg_len de cmsghdr). À la réception d'erreur, msghdr sera rempli avec MSG_ERRQUEUE. Après la lecture d'une erreur, l'état de la socket est modifié d'après l'erreur suivante dans la file. L'appel recvmsg utilise une structure msghdr pour minimiser le nombre de paramètres à fournir directement. Cette structure a la forme suivante, définie dans <sys/socket.h> struct msghdr { caddr_t msg_name; /* optional address */ u_int msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ u_int msg_iovlen; /* # elements in msg_iov */ caddr_t msg_control; /* ancillary data, see below */ u_int msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ }; Ici msg_name et msg_namelen spécifient l'adresse d'origine si le socket n'est pas connecté, msg_name peut être un pointeur nul si le nom n'est pas nécessaire. msg_iov et msg_iovlen décrivent les buffers de réception comme décrit dans readv(2). msg_control, de longueur msg_controllen, pointe sur un buffer utilisé pour les autres messages relatifs au protocole, ou à d'autres données annexes. Lorsqu'on invoque recvmsg, msg_controllen doit contenir la longueur disponible dans le buffer msg_control; au retour il contiendra la longueur de la séquence de message de contrôle. Les messages ont la forme struct cmsghdr { u_int cmsg_len; /* data byte count, including hdr */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by u_char cmsg_data[]; */ }; Les données de service ne doivent être manipulées qu'avec les macros de cmsg(3) À titre d'exemple, Linux utilise ce mécanisme pour transmettre des erreurs étendues, des options IP, ou des descripteurs de fichiers sur des sockets Unix. Le champ msg_flags du msghdr est rempli au retour de recvmsg(). Il peut contenir plusieurs attributs : MSG_EOR indique une fin d'enregistrement, les données reçues terminent un enregistrement (utilisé généralement avec les sockets du type SOCK_SEQPACKET). MSG_TRUNC indique que la portion finale du datagramme a été abandonnée car le datagramme était trop long pour le buffer fourni. MSG_CTRUNC indique que des données de contrôle ont été abandonnées à cause d'un manque de place dans le buffer de données annexes. MSG_OOB indique que des données hors-bande ont été reçues. MSG_ERRQUEUE indique qu'aucune donnée n'a été reçue, sauf une erreur étendue depuis la file d'erreurs. MSG_DONTWAIT Activer les opérations non-bloquantes. Si l'opération devait bloquer, EAGAIN serait renvoyé (on peut aussi activer ce comportement avec l'option O_NONBLOCK de la fonction F_SETFL de fcntl(2). VALEUR RENVOYÉE Ces fonctions renvoient le nombre d'octets reçus si elles réussissent, ou -1 si elles échouent, auquel cas errno contient le code d'erreur. ERREURS Il y a des erreurs standards déclenchées par le niveau socket, et des erreurs supplémentaires spécifiques aux protocoles. Voyez leurs pages de manuel. EBADF L'argument s n'est pas un descripteur valide. ECONNREFUSED Un hôte distant à refusé la connection réseau (généralement parce qu'il n'offre pas le service demandé). ENOTCONN Le socket est associé à un protocole orienté connection et n'a pas encore été connectée (voir connect() et accept()). ENOTSOCK L'argument s ne correspond pas à une socket. EAGAIN Le socket est non-bloquant et aucune donnée n'est disponible, ou un délai de timeout a été indiqué, et il a expiré sans que l'on ait reçu quoi que ce soit. EINTR Un signal a interrompu la lecture avant que des données soient disponibles. EFAULT Un buffer pointe en dehors de l'espace d'adressage accessible. EINVAL un argument est invalide. CONFORMITÉ 4.4 BSD (ces fonctions sont apparues dans BSD 4.2). NOTE Les prototypes fournis concernent la GlibC 2. Les Spécifications Single Unix les définissent, mais le type de retour est `ssize_t' (alors que BSD 4.*, libc4 , et libc5 renvoient un 'int'). L'argument flags est un 'int' dans BSD 4.*, mais `unsigned int' dans libc4 et libc5. L'argument len est un `int' dans BSD 4.*, mais un 'size_t' dans libc4 et libc5. L'argument fromlen est un int *' dans BSD 4.*, libc4 et libc5. Le type 'socklen_t *' a été inventé par POSIX. VOIR AUSSI accept(). fcntl(2), read(2), select(), getsockopt(2),socket(), cmsg(3) TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

close()

close - Fermer un descripteur de fichier. SYNOPSIS
#include <unistd.h>
int close(int fd);
DESCRIPTION close ferme le descripteur fd, de manière à ce qu'il ne référence plus aucun fichier, et puisse être réutilisé. Tous les verrouillages sur le fichier qui lui étaient associés, appartenant au processus, sont supprimés (quel que soit le descripteur qui fut utilisé pour obtenir le verrouillage). Si fd est la dernière copie d'un descripteur de fichier donné, les ressources qui lui sont associées sont libérées. Si le descripteur était la dernière référence sur un fichier supprimé avec unlink(2), le fichier est effectivement effacé. VALEUR RENVOYÉE close renvoie 0 s'il réussit, ou -1 en cas d'échec, auquel cas errno contient le code d'erreur. ERREURS EBADF Le descripteur de fichier fd est invalide EINTR L'appel-système close() a été interrompu par un signal. EIO Une erreur d'entrée-sortie s'est produit. CONFORMITÉ SVr4, SVID, POSIX, X/OPEN, BSD 4.3. SVr4 documente une condition d'erreur supplémentaire ENOLINK. NOTES Ne pas vérifier la valeur de retour de close est une pratique courante mais également une grave erreur de programmation. Il est possible qu'une erreur correspondant à un appel write(2) antérieur ne soit rapportée que lors du close final. Ne pas vérifier la valeur de retour lorsque l'on ferme un fichier peut conduire à une perte silencieuse de données. Ceci est principalement vrai dans le cas de systèmes de fichiers NFS, ou avec l'utilisation des quotas de disques. Une fermeture sans erreur ne garantit pas que les données on été vraiment écrites sur le disque, car le noyau repousse les écritures le plus tard possible. Il n'est pas fréquent qu'un système de fichiers vide les buffers dès la fermeture d'un flux. Si vous désirez vous assurer que les informations sont en sûreté sur le disque, utilisez fsync(2) (mais des considérations matérielles entrent en jeu à ce moment). VOIR AUSSI open(2), fcntl(2), shutdown(), unlink(2), fclose(3). fsync(3). TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

shutdown()

shutdown - Terminer une commnication en full-duplex. SYNOPSIS
#include <sys/socket.h>
int shutdown(int s, int how);
DESCRIPTION La fonction shutdown termine tout ou partie d'une connexion full-duplex sur le socket s. Si how vaut SHUT_RD, la réception est désactivée. Si how vaut SHUT_WR, l'émission est désactivée. Si how vaut SHUT_RDWR, l'emission et la réception sont désactivées. VALEUR RENVOYÉE shutdown renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur. ERREURS EBADF s n'est pas un descripteur valide. ENOTSOCK s est un fichier, pas un socket. ENOTCONN La socket s n'est pas connectée. NOTES Les constantes SHUT_RD, SHUT_WR, SHUT_RDWR ont pour valeur 0, 1, et 2 respectivement, et sont définies dans <sys/socket.h> depuis la GlibC-2.1.91. CONFORMITÉ BSD 4.4, la fonction shutdown est apparue dans BSD 4.2. VOIR AUSSI connect(), socket() TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

htonl, htons, ntohl, ntohs

Conversions d'ordre des octets entre un hôte et un réseau. SYNOPSIS
#include <netinet/in.h>
uint32_t htonl (uint32_t hostlong); uint16_t htons (uint16_t hostshort); uint32_t ntohl (uint32_t netlong); uint16_t ntohs (uint16_t netshort);
DESCRIPTION htonl() convertit un entier non-signé depuis l'ordre des octets de l'hôte vers celui du réseau. htons() convertit un entier court non-signé depuis l'ordre des octets de l'hôte vers celui du réseau. ntohl() convertit un entier non-signé depuis l'ordre des octets du réseau vers celui de l'hôte. ntohs() convertit un entier court non-signé depuis l'ordre des octets du réseau vers celui de l'hôte. Sur les i80x86, l'ordre des octets de l'hôte est LSB (Least Significant Byte first), c'est à dire octet de poids faible en premier, alors que sur les réseaux, notamment l'Internet, l'ordre est MSB (Most Significant Byte first) octet de poids fort en premier. CONFORMITÉ BSD 4.3 VOIR AUSSI gethostbyname(3), getservent(3) TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006
GETSERVENT(3) Manuel du programmeur Linux GETSERVENT(3) NOM getservent, getservbyname, getservbyport, setservent, endservent - Accéder aux informations sur les services. SYNOPSIS #include struct servent *getservent (void); struct servent *getservbyname (const char *name, const char *proto); struct servent *getservbyport (int port, const char *proto); void setservent (int stayopen); void endservent (void); DESCRIPTION La fonction getservent() lit l'enregistrement suivant du fichier /etc/services et renvoie une structure servent contenant les divers champs de l'enregistrement. Le fichier /etc/services est ouvert si besoin est. La fonction getservbyname() renvoie une structure servent pour l'enreg- istrement du fichier /etc/services qui correspond au service nommé name et utilisant le protocole proto. Si proto est NULL, n'importe quel pro- tocole sera accepté. La fonction getservbyport() renvoie une structure servent pour l'enreg- istrement correspondant au port indiqué (dans l'ordre des octets du réseau) et utilisant le protocole proto. Si proto est NULL, n'importe quel protocole sera accepté. La fonction setservent() ouvre, et ramène au début le pointeur du fichier /etc/services. Si stayopen est vrai (valant 1), alors le fichiers ne sera pas refermé entre les appels successifs à getservby- name() et getservbyport(). la fonction endservent() ferme le fichier /etc/services. La structure servent est définie dans ainsi : struct servent { char *s_name; /* Nom officiel du service */ char **s_aliases; /* Liste d'alias */ int s_port; /* Numéro de port */ char *s_proto; /* Protocole utilisé */ } Les membres de la structure servent sont : La fonction getservbyport() renvoie une structure servent pour l'enreg- istrement correspondant au port indiqué (dans l'ordre des octets du réseau) et utilisant le protocole proto. Si proto est NULL, n'importe quel protocole sera accepté. La fonction setservent() ouvre, et ramène au début le pointeur du fichier /etc/services. Si stayopen est vrai (valant 1), alors le fichiers ne sera pas refermé entre les appels successifs à getservby- name() et getservbyport(). la fonction endservent() ferme le fichier /etc/services. La structure servent est définie dans ainsi : struct servent { char *s_name; /* Nom officiel du service */ char **s_aliases; /* Liste d'alias */ int s_port; /* Numéro de port */ char *s_proto; /* Protocole utilisé */ } Les membres de la structure servent sont : s_name Le nom officiel du service. s_aliases Une liste terminée par zéro contenant d'autres noms utilisables pour le service. s_port Le numéro de port, donné dans l'ordre des octets du réseau. s_proto Le nom du protocole utilisé par ce service. VALEUR RENVOYÉE Les fonctions getservent(), getservbyname() et getservbyport() ren- voient une structure servent, ou un pointeur NULL si une erreur se pro- duit, ou si la fin du fichier est atteinte. FICHIERS /etc/services Base de données des services. CONFORMITÉ BSD 4.3 VOIR AUSSI getprotoent(3), getnetent(3), services(5) SHTRADUCTION Christophe Blaess, 1996-2003.

gethostbyname() gethostbyaddr()

Obtenir des informations sur le serveur d' après son adresse ou son IP. SYNOPSIS
#include <netdb.h> extern int h_errno; struct hostent *gethostbyname(const char *name); #include <sys/socket.h> /* pour AF_INET */
struct hostent *gethostbyaddr(const char *addr, int len, int type);
DESCRIPTION La fonction gethostbyname() renvoie une structure de type hostent pour l'hôte name. La chaîne name est soit un nom d'hôte, soit une adresse IPv4 en notation pointée standard, soit une adresse IPv6 avec la notation points-virgules et points (Cf RFC 1884 pour la description des adresses IPv6). S name est une adresse IPv4 ou IPv6, aucune recherche supplémentaire n'a lieu et gethostbyname() copie simplement la chaine name dans le champ h_name et le champs équivalent struct in_addr dans le champs h_addr_list[0] de la structure hostent renvoyée. Si name ne se termine pas par un point, et si la variable d'environnement HOSTALIASES est configurée, le fichier d'alias indiqué par HOSTALIASES sera d'abord parcouru à la recherche de name (voir hostname(7) pour le format du fichier). Le domaine courant et ses parents sont parcourus si name ne se termine pas par un point. La fonction gethostbyaddr() renvoie une structure du type hostent pour l'hôte d'adresse addr. Cette adresse est de longueur len et du type donné. Le seul type d'adresse valide est actuellement AF_INET. Les interrogations du serveur de noms effectuées par gethostbyname() et gethostbyaddr() utilisent les éléments suivants : le serveur de noms named(8), les lignes de /etc/hosts, et l'annuaire Network Information Service (NIS ou YP), suivant le contenu de la ligne order du fichier /etc/host.conf. (Voir resolv+(8)). L'action par défaut consiste à interroger named(8), puis /etc/hosts. La structure hostent est définie dans <netdb.h>. VALEUR RENVOYÉE Les fonctions gethostbyname() et gethostbyaddr() renvoient un pointeur sur la structure hostent, ou bien un pointeur NULL si une erreur se produit, auquel cas h_errno contient le code d'erreur. ERREURS La variable h_errno peut prendre les valeurs suivantes : HOST_NOT_FOUND L'hôte indiqué est inconnu. NO_ADDRESS ou NO_DATA Le nom est valide mais ne possède pas d'adresse IP. NO_RECOVERY Une erreur fatale du serveur de noms est apparue. TRY_AGAIN Une erreur temporaire du serveur de noms est apparue, essayez un peu plus tard. FICHIERS /etc/host.conf Fichier de configuration de la résolution de noms. /etc/hosts Base de données des hôtes. CONFORMITÉ BSD 4.3 NOTES Les spécifications SUS-v2 déclarent -à tort- le paramètre len de gethostbyaddr() de type size_t. (Ceci est erroné car il doit obligatoirement être un int, ce que size_t n'est pas toujours. POSIX 1003.1-2001 le déclare socklen_t, ce qui est correct). Les fonctions gethostbyname() et gethostbyaddr() peuvent renvoyer des pointeurs sur des données statiques susceptibles d'être écrasées d'un appel à l'autre. Copier la structure struct hostent ne suffit pas car elle contient elle-même des pointeurs. Une copie en profondeur est indispensable. La GlibC 2 propose aussi une fonction gethostbyname2() qui agit comme gethostbyname(), qui permet de préciser la famille à laquelle l'adresse doit appartenir. La GlibC 2 propose aussi les versions réentrantes gethostbyname_r() et gethostbyname2_r(). Elles renvoient zéro si elles réussissent et une valeur non-nulle en cas d'erreur. Le résultat de l'appel est stocké dans la structure d'adresse ret. Après l'appel, *result vaudra NULL en cas d'erreur, ou pointera sur le résultat. Des données auxiliaires seront stockées dans le buffer buf de longueur buflen. (Si le buffer est trop petit, ces fonctions renverront ERANGE). La variable h_errno n'est pas modifiée, mais l'adresse d'une variable où stocker le code d'erreur est transmis dans h_errnop. POSIX 1003.1-2001 indique gethostbyaddr() et gethostbyname() comme légaux, et introduit struct hostent * getipnodebyaddr (const void * restrict addr, socklen_t len, int type, int * restrict error_num); struct hostent *getipnodebyname (const char * name, int type, int flags, int * error_num); VOIR AUSSI resolver(3), hosts(5), hostname(7), resolv+(8), named(8). TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

getsockname()

Obtenir le nom d'un socket. SYNOPSIS
#include <sys/socket.h>
int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
DESCRIPTION Getsockname renvoie le nom name du socket indiqué. Le paramètre namelen doit être initialisé pour indiquer la taille de la zone mémoire pointée par name. En retour, il contiendra la taille effective (en octets) du nom renvoyé. VALEUR RENVOYÉE getsockname renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur. ERREURS EBADF L'argument s n'est pas un descripteur valide. ENOTSOCK L'argument s est un fichier, pas une socket. ENOBUFS Pas assez de mémoire pour le noyau EFAULT name pointe en dehors de l'espace d'adressage accessible. CONFORMITÉ SVr4, BSD 4.4 (la fonction getsockname est apparue dans BSD 4.2). SVr4 documente des conditions d'erreur ENOMEM et ENOSR supplémentaires. NOTE Le troisième argument de getsockname est en fait un int (et c'est ce qu'utilisent BSD 4.*, libc4 et libc5). Une certaine confusion POSIX résulte du "socklen_t" actuel. Les propositions de standard n'ont pas encore été adoptées, mais glibc2 les suit déjà et utilise socklen_t. Pour plus de détails voir accept(). VOIR AUSSI bind(), socket() TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

getpeername()

getpeername - Obtenir le nom d'un correspondant connecté sur un socket. SYNOPSIS
#include <sys/socket.h>
int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
DESCRIPTION La fonction getpeername renvoie le nom du correspondant connecté sur un socket s. Le paramètre namelen doit être initialisé pour indiquer la taille de la zone pointée par name. En retour, il contiendra la longueur effective (en octets) du nom retourné. Le nom est tronqué si le buffer est trop petit. VALEUR RENVOYÉE getpeername renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur. ERREURS EBADF L'argument s n'est pas un descripteur valide. ENOTSOCK L'argument s est un fichier, pas une socket. ENOTCONN Le socket n'est pas connectée. ENOBUFS Pas assez de mémoire pour le noyau EFAULT name pointe en dehors de l'espace d'adressage accessible CONFORMITÉ SVr4, 4.4BSD (La fonction getpeername est apparue dans BSD 4.2). NOTE Le troisième argument de getpeername est en fait un int (et c'est ce qu'utilisent BSD 4.*, libc4 et libc5). Une certaine confusion POSIX résulte du "socklen_t" actuel. Les propositions de standard n'ont pas encore été adoptées, mais glibc2 les suit déjà et utilise socklen_t. Pour plus de détails voir accept(). VOIR AUSSI accept(), bind(), getsockname() TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006

4) Stratégies des serveurs

Accept() et recv() bloquent l' application jusqu'à ce qu'ils aient terminé leur action. Alors comment faire si vous voulez faire autre chose pendant que vous attendez?

3.1 Sockets non-bloquants

Emploi de fcntl()
#include <fcntl.h> #include <unistd.h> #include <errno.h> #include <stdio.h> int rc; char buf[128]; fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); errno = 0; rc = read(STDIN_FILENO, buf, 127); if (rc == -1) { if (errno == EAGAIN) { // no input available } else { perror("read()"); } } buf[rc] = 0; printf("%s", buf);

3.2 Forking servers

fork() crée un processus fils pour chaque nouveau client. Un processus hérite des propriétés du parent, la mémoire est dupliquée sur modification. Un processus ne communique pas avec les autres processus. Trop de processus sont susceptibles d' alourdir le système. La commande ps -u permet de voir les processus d'un utilisateur. for (;;) { newsockfd = accept(sockfd, &that, &lg); if (newsockfd < 0) /* accept error */ if ((pid = fork()) < 0) /* fork error ! */ if (pid == 0) { close(sockfd); /* traiter newsockfd */ close(newsockfd); exit(0); } close(newsockfd); } Exemples

3.3 Multithread

Créer un process par client avec fork peut se révéler lourd à gérer pour le système. Les threads font partie du même et unique process et partagent le même espace, ils sont nommés "processus légers". Un processus léger est plus rapide à créer qu'un fork, plus rapide à détruire aussi. Un serveur multi-thread crée un pthread par client. Le thread se termine quand le client quitte. Gérer avec des sémaphores et pthread_mutex_lock/unlock. Exemple

3.4 Multiplexage d'entrées/sorties synchrones. select(), pselect(), FD_CLR(), FD_ISSET(), FD_SET(), FD_ZERO()

Demander au système de nous dire quand un socket est prêt, pour le traiter à ce moment-là. C'est ce que fait select(). SYNOPSIS
/* D'après Posix 1003.1-2001 */ #include <sys/select.h> /* D'après les standards précédents */ #include <sys/time.h> #include <sys/types.h> #include <unistd.h>
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t * sigmask); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set);
DESCRIPTION Les fonctions select et pselect attendent des changements d'état sur plusieurs descripteurs de fichiers. Elles ont un comportement identique, avec trois différences : (i) La fonction select utilise un délai exprimé avec une struct timeval (secondes et microsecondes), alors que pselect utilise une struct timespec (secondes et nanosecondes). (ii) La fonction select peut modifier le paramètre timeout pour indiquer le temps restant. La fonction pselect ne change pas ce paramètre. (iii) La fonction select n'a pas de paramètre sigmask et se comporte comme pselect avec une valeur NULL pour sigmask Il y a trois ensembles indépendants de descripteurs surveillés simultanément. Ceux de l'ensemble readfds seront surveillés pour vérifier si des caractères deviennent disponibles en lecture. Plus précisément, on vérifie si un appel-système de lecture ne bloquera pas - en particulier un descripteur en fin-de-fichier sera considéré comme prêt. Les descripteurs de l'ensemble writefds seront surveillés pour vérifier si une écriture ne bloquera pas. Ceux de exceptfds seront surveillés pour l'occurrence de conditions exceptionnelles. En sortie, les ensembles sont modifiés pour indiquer les descripteurs qui ont changé de statut. Quatre macros sont disponibles pour la manipulation des ensembles FD_ZERO efface un ensemble. FD_SET et FD_CLR ajoutent et suppriment un descripteur dans un ensemble. FD_ISSET vérifie si un descripteur est contenu dans un ensemble, principalement utile après le retour de select. n est le numéro du plus grand descripteur des 3 ensembles, plus 1. timeout est une limite supérieure au temps passé dans select avant son retour. Elle peut être nulle, ce qui conduit select à revenir immédiatement. (Ce qui sert pour des surveillance en polling). Si le timeout est NULL (aucun), select peut bloquer indéfiniment. sigmask est un pointeur sur un masque de signaux (voir sigprocmask(2)). S'il n'est pas NULL, alors pselect remplace d'abord le masque de signaux en cours par celui indiqué dans sigmask, puis invoque la fonction `select', et enfin restaure le masque de signaux à nouveau. L'idée derrière pselect est que pour l'attente d'un évènement, que ce soit un signal ou une condition sur un descripteur, un test atomique est nécessaire pour éviter les situations de concurrence. (Supposons que le gestionnaire de signaux active un drapeau global et revienne. Alors un test de ce drapeaux, suivi d'un appel select() peut bloquer indéfiniment si le signal arrive juste après le test mais avant l'appel. A l'inverse, pselect permet de bloquer le signal d'abord, traiter les signaux déjà reçus, puis invoquer pselect() avec le sigmask, désiré, en évitant la situation de blocage). Comme Linux n'a pas encore d'appel système pselect(), la routine de la bibliothèque GlibC 2 le contient encore par défaut. Délai maximal Les structures temporelles concernées sont définies dans <sys/time.h> comme ceci :
             struct timeval {
                  long    tv_sec;         /* secondes      */
                  long    tv_usec;        /* microsecondes */
              };
	et
              struct timespec {
                  long    tv_sec;         /* secondes     */
                  long    tv_nsec;        /* nanosecondes */
              };
(Cependant, voir plus bas pour les versions POSIX 1003.1-2001) Certaines applications appellent select nul, et un délai timeout non nul, afin d'endormir, de manière portable, le processus avec une précision plus fine que la seconde. Sous Linux, la fonction select modifie timeout pour indiquer le temps restant mais la plupart des autres implémentations ne le font pas. Ceci pose des problèmes à la fois pour porter sur d'autres systèmes du code développé sous Linux qui utilise cette valeur de timeout modifiée, et pour porter sous Linux du code qui réutilise plusieurs fois la structure timeval sans la réinitialiser. La meilleure attitude à adopter est de considérer timeout comme indéfini après le retour de select. VALEUR RENVOYÉE En cas de réussite select et pselect renvoient le nombre de descripteurs dans les ensembles, qui peut être nul si le délai de timeout a expiré avant que quoi que ce soit d'intéressant ne se produise. Ils retournent -1 s'ils échouent, auquel cas errno contient le code d'erreur. ERREURS EBADF Un descripteur de fichier (dans l'un des ensembles) est invalide. EINTR Un signal a été intercepté. EINVAL n est négatif ENOMEM Pas assez de mémoire pour le noyau. NOTES avec les trois ensembles vides, n EXEMPLE
	#include <stdio.h>
	#include <sys/time.h>
	#include <sys/types.h>
	#include <unistd.h>
	
	int main(void)
	{
	  fd_set rfds;
	  struct timeval tv;
	  int retval;

	  /* Surveiller stdin (fd 0) en attente d'entrées */
	  FD_ZERO(&rfds);
	  FD_SET(0, &rfds);
	  /* Pendant 5 secondes maxi */
	  tv.tv_sec = 5;
	  tv.tv_usec = 0;

	  retval = select(1, &rfds, NULL, NULL, &tv);
	  /* Considérer tv comme indéfini maintenant ! */
	  struct timeval tv;
	  int retval;

	  /* Surveiller stdin (fd 0) en attente d'entrées */
	  FD_ZERO(&rfds);
	  FD_SET(0, &rfds);
	  /* Pendant 5 secondes maxi */
	  tv.tv_sec = 5;
	  tv.tv_usec = 0;

	  retval = select(1, &rfds, NULL, NULL, &tv);
	  /* Considérer tv comme indéfini maintenant ! */

	  if (retval)
	    printf("Données disponibles maintenant\n");
	    /* FD_ISSET(0, &rfds) est vrai */
	  else
	    printf("Pas de données depuis 5 secondes\n");

	  return (0);
	}
CONFORMITÉ BSD 4.4 (la fonction select est apparue dans BSD 4.2). Généralement portable depuis ou vers des systèmes non-BSD supportant des clones de la couche sockets BSD (y compris les variantes du Système V). Néanmoins, sachez que les variantes du système V fixent une variable de timeout avant le retour alors que les variantes BSD ne le font pas. La fonction pselect est définie dans IEEE Std 1003.1g-2000 (POSIX.1g) et pour partie dans POSIX 1003.1-2001. On la trouve dans GlibC 2.1 et ultérieur. Glibc2.0 a une fonction de ce nom, mais sans le paramètre sigmask. NOTES Un ensemble fd_set est un buffer de taille fixe. Exécuter FD_CLR ou FD_SET avec fd négatif ou supérieur ou égal à FD_SETSIZE résultera en un comportement indéfini. Plus encore, POSIX demande que fd soit un descripteur de fichier valide. En ce qui concerne les types impliqués, la situation classique est que les deux champs de la structure timeval soient des "long" (comme ci-dessous), et que la structure soit définie dans struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ };
Avec la structure définie dans <sys/select.h> et les types de données time_t et suseconds_t définis dans <sys/types.h>. Concernant les prototypes, on demande classiquement l'inclusion de <time.h> pour select. Avec POSIX 1003.1-2001, on préfère inclure <sys/select.h> pour select et pselect. Les bibliothèques libc4 et libc5 n'avaient pas d'en-tête <sys/select.h>, mais avec les GlibC 2.0 et suivantes le fichier existe. Pour la GlibC 2.0, le prototype de pselecT est toujours erroné. Avec la GlibC 2.1-2.2.1 le prototype de pselect est fourni si la constante _GNU_SOURCE est définie avant l'inclusion. Avec GlibC 2.2.2-2.2.4, il faut que la constante _XOPEN_SOURCE soit définie avec une valeur supérieure ou égale à 600. Quoiqu'il en soit, depuis POSIX 1003.1-2001, le bon prototype devrait être défini par défaut. VOIR AUSSI Pour un tutoriel avec des exemples, voir select_tut(2). D'autres pages ayant un vague rapport : accept(), connect(), poll(2), read(2), recv(), send(), sigprocmask(2), write(2) TRADUCTION Christophe Blaess, 1996-2003. Bertrand Massot 2006
De l' intérêt de select() select() est un appel système qui demande au noyau de se mettre en attente d'un évènement sur un ensemble de descripteurs de fichiers. Ce nombre est de FD_SETSIZE, généralement 1024 par process. Pour voir à combien s'évalue ce chiffre sur votre système: printf("%d\n", FD_SETSIZE); Un ensemble fd_set est un ensemble de descripteurs de fichiers. Concrètement c'est une table d'index par lequel le système interagit avec votre programme. Pour créer un fd_set, il faut lui réserver son espace en mémoire fd_set fd; Puis le préparer en le remplissant de zeros en invoquant la fonction FD_ZERO(&fd); Les trois premiers descripteurs du fd_set correspondent à l'entrée standard, la sortie standard, et la sortie d' erreurs, qui ont respectivement les index 0, 1, et 2. C'est pourquoi le premier descripteur ajouté au fd_set aura toujours l' index 3. Pour ajouter un descripteur de fichier au fd_set, il faut invoquer la fonction FD_SET(int fd, fd_set *set); et pour retirer un descripteur: FD_CLR(int fd, fd_set *set); Lorsque le fd_set est prêt, la fonction select int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ... Demande de surveiller simultanément trois ensembles fd_set: un en écriture, un en lecture, et un pour les exceptions. Les index contenus dans chaque fd_set indiquent quels descripteurs sont valides pour chaque opération. En d' autres termes chaque fd_set est constitué de 1024 bits, chaque bit représentant un descripteur, sa valeur étant 0 ou 1: disponible ou pas (voir dans fs/select.c comment les bits sont organisés par le noyau). n est égal à la valeur du plus grand descripteur, +1. Il est conseillé de garder cette valeur dans une variable pour ne pas la perdre. Chaque appel à la fonction select vide chaque fd_set pour ne lui laisser que les descripteurs qui sont disponibles. La fonction FD_ISSET(int fd, fd_set *set); vérifie qu'un descripteur est toujours présent dans l' ensemble. En effet il ne l'est plus lorsque le fichier est fermé -ou la connection du socket rompue, FD_ISSET retourne alors zero.
  int largest = 0;
  FD_ZERO(&f);
  for (i = 0; i < number_of_sockets; ++i)
  {
    FD_SET(sockets[i], &f);
    if (largest < sockets[i])
    {
     largest = sockets[i];
    }
  }
  rc = select(largest + 1, &f, NULL, NULL, &t);
  if (rc == -1)
  {
    //select() error
  }
  else if (rc == 0)
  {
    //no socket ready
  }
  else
  {
    for (i = 0; i < number_of_sockets; ++i)
    {
      if (FD_ISSET(sockets[i], &f))
      {
        //input available
      }
    }
  }
Extrait de pages Man (select_tut) : "Multiplexage d'entrées/sorties synchrones." "... select gère le problème épineux des flux de données bidirectionnels simultanés. Vous pourriez penser qu'il est plus efficace d'utiliser un appel fork() et de dédier une tâche à chaque flux. Cela devient alors plus délicat que vous ne l'imaginez. Une autre idée est de configurer les E/S comme non bloquantes en utilisant un appel ioctl(). Cela pose également problème parce que vous finissez par avoir des timeouts inefficaces. "Certaines personnes considèrent select comme une fonction ésotérique et rarement utilisée. En fait, de nombreux types de programmes deviennent extrêmement compliqués sans cette fonction. select peut être utilisé pour résoudre de façon portable et efficace de nombreux problèmes que des programmeurs naïfs essaient de résoudre avec des threads, des forks, des IPCs, des signaux, des mémoires partagées et d'autres méthodes peu élégantes. Pour résumer, select surveille simplement de multiples descripteurs de fichiers, et constitue l'appel Unix standard pour réaliser cette tâche. Quelle est donc la finalité de select? Ne puis-je pas simplement lire et écrire dans les descripteurs chaque fois que je le souhaite? L'objet de select est de surveiller de multiples descripteurs simultanément et d'endormir proprement le processus s'il n'y a pas d'activité. Il fait ceci tout en vous permettant de gérer de multiples tubes et sockets simultanément. Les programmeurs UNIX se retrouvent souvent dans une situation dans laquelle ils doivent gérer des E/S provenant de plus d'un descripteur de fichier et dans laquelle le flux de données est intermittent. Si vous deviez créer une séquence d'appels read et write, vous vous retrouveriez potentiellement bloqué sur un de vos appels attendant pour lire ou écrire des données à partir/vers un descripteur de fichier, alors qu'un autre descripteur de fichier est inutilisé bien qu'il soit disponible pour lire/écrire des données. select gère efficacement cette situation." Attention, sous windows:
	typedef struct fd_set {
		u_int fd_count;                /* how many are SET? */
		SOCKET  fd_array[FD_SETSIZE];  /* an array of SOCKETs */
	} fd_set; 
Exemple


Différences entre les sockets Win32
et les sockets BSD

#ifdef WIN32
 #include <winsock2.h>
 #define bzero(a,b) memset (a,0,b)
#else
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/un.h>
 #include <arpa/inet.h>
 #include <unistd.h>
 #define closesocket(s) close((SOCKET)a)
#endif

 main
  #ifdef WIN32
    WSADATA wsa_data;
    int err = WSAStartup (MAKEWORD (2, 2), &wsa_data);
    if (WSAStartup (MAKEWORD (2, 2), &wsa_data)==0)
    {
      puts ("WSAStartup 2 2 failed\n.");
      exit(1);
    }
  #endif

  // ...

  #ifdef WIN32
    WSACleanup();
  #endif


Win			UNIX
SOCKET			int
SOCKADDR		struct sockaddr_in
closesocket()		close()
memset (a,0,b)		bzero(a,b)
25/12/06 2018 Bertrand Massot