Using sockets for inter-process communication
Utilisation de sockets pour la communication interprocessus
The socket is a powerful abstraction as it allows processes to communicate even if they are located on different computers. In this specific cases, the inter-processes communication will go through a network.
Le socket est une abstraction puissante car il permet aux processus de communiquer même s'ils sont situés sur des ordinateurs différents. Dans ce cas précis, la communication inter-processus passe par un réseau.
Networked applications were usually implemented by using the :term:`socket` :term:`API`. This API was designed when TCP/IP was first implemented in the `Unix BSD`_ operating system [Sechrest]_ [LFJLMT]_, and has served as the model for many APIs between applications and the networking stack in an operating system. Although the socket API is very popular, other APIs have also been developed. For example, the STREAMS API has been added to several Unix System V variants [Rago1993]_. The socket API is supported by most programming languages and several textbooks have been devoted to it. Users of the C language can consult [DC2009]_, [Stevens1998]_, [SFR2004]_ or [Kerrisk2010]_. The Java implementation of the socket API is described in [CD2008]_ and in the `Java tutorial <http://java.sun.com/docs/books/tutorial/networking/sockets/index.html>`_. In this section, we will use the C socket API to illustrate the key concepts.
Les applications en réseau sont généralement mises en œuvre à l'aide de l'interface :term:`socket` :term:`API`. Cette API a été conçue lorsque TCP/IP a été implémenté pour la première fois dans le système d'exploitation `Unix BSD`_ [Sechrest]_ [LFJLMT]_, et a servi de modèle pour de nombreuses API entre les applications et la pile réseau dans un système d'exploitation. Bien que l'API socket soit très populaire, d'autres API ont également été développées. Par exemple, l'API STREAMS a été ajoutée à plusieurs variantes d'Unix System V [Rago1993]_. L'API socket est supportée par la plupart des langages de programmation et plusieurs manuels lui ont été consacrés. Les utilisateurs du langage C peuvent consulter [DC2009]_, [Stevens1998]_, [SFR2004]_ ou [Kerrisk2010]_. L'implémentation Java de l'API socket est décrite dans [CD2008]_ et dans le `tutoriel Java <http://java.sun.com/docs/books/tutorial/networking/sockets/index.html>`_. Dans cette section, nous utiliserons l'API socket C pour illustrer les concepts clés.
The socket API is quite low-level and should be used only when you need a complete control of the network access. If your application simply needs, for instance, to retrieve data from a web server, there are much simpler and higher-level APIs.
L'API socket est de très bas niveau et ne doit être utilisée que lorsque vous avez besoin d'un contrôle complet de l'accès au réseau. Si votre application a simplement besoin, par exemple, d'extraire des données d'un serveur web, il existe des API beaucoup plus simples et de plus haut niveau.
A detailed discussion of the socket API is outside the scope of this section and the references cited above provide a detailed discussion of all the details of the socket API. As a starting point, it is interesting to compare the socket API with the service primitives that we have discussed in the previous chapter. Let us first consider the connectionless service that consists of the following two primitives :
Une discussion détaillée de l'API de socket sort du cadre de cette section et les références citées ci-dessus fournissent une discussion détaillée de tous les détails de l'API de socket. Pour commencer, il est intéressant de comparer l'API socket avec les primitives de service que nous avons abordées dans le chapitre précédent. Considérons d'abord le service sans connexion qui se compose des deux primitives suivantes :
`DATA.request(destination,message)` is used to send a message to a specified destination. In this socket API, this corresponds to the ``send`` method.
`DATA.request(destination,message)` est utilisé pour envoyer un message à une destination spécifiée. Dans cette API socket, cela correspond à la méthode ``send``.
`DATA.indication(message)` is issued by the transport service to deliver a message to the application. In the socket API, this corresponds to the return of the ``recv`` method that is called by the application.
`DATA.indication(message)` est émis par le service de transport pour délivrer un message à l'application. Dans l'API socket, cela correspond au retour de la méthode ``recv`` qui est appelée par l'application.
The `DATA` primitives are exchanged through a service access point. In the socket API, the equivalent to the service access point is the `socket`. A `socket` is a data structure which is maintained by the networking stack and is used by the application every time it needs to send or receive data through the networking stack.
Les primitives `DATA` sont échangées via un point d'accès au service. Dans l'API socket, l'équivalent du point d'accès au service est le `socket`. Une `socket` est une structure de données qui est maintenue par la pile réseau et qui est utilisée par l'application à chaque fois qu'elle doit envoyer ou recevoir des données à travers la pile réseau.
Sending data to a peer using a socket
Envoi de données à un pair à l'aide d'une socket
The ``sendto`` system call allows to send data to a peer identified by its socket address through a given socket.
L'appel système ``sendto`` permet d'envoyer des données à un pair identifié par son adresse de socket via un socket donné.
The first argument is the file descriptor of the socket that we use to perform the communication. ``buf`` is a buffer of length ``len`` containing the bytes to send to the peer. The usage of ``flags`` argument is out of the scope of this section and can be set to 0. ``dest_addr`` is the socket address of the destination to which we want to send the bytes, its length is passed using the ``addrlen`` argument.
Le premier argument est le descripteur de fichier de la socket que nous utilisons pour effectuer la communication. ``buf`` est un tampon de longueur ``len`` contenant les octets à envoyer à l'homologue. L'utilisation de l'argument ``flags`` est hors de portée de cette section et peut être mis à 0. ``dest_addr`` est l'adresse de la socket de destination à laquelle nous voulons envoyer les octets, sa longueur est passée en utilisant l'argument ``addrlen`'.
In the following example, a C program is sending the bytes ``'h'``, ``'e'``, ``'l'``, ``'l'`` and ``'o'`` to a remote process located at address ``peer_addr``, using the already created socket ``sock``.
Dans l'exemple suivant, un programme C envoie les octets ``'h'``, ``'e'``, ``'l'``, ``'l'`` et ``'o'`` à un processus distant situé à l'adresse ``peer_addr``, en utilisant la socket ``sock`` déjà créée.
As the ``sendto`` function is generic, this function will work correctly independently from the fact that the peer's address is defined as a path on the computer filesystem or a network address.
Comme la fonction ``sendto`` est générique, cette fonction fonctionnera correctement indépendamment du fait que l'adresse de l'homologue soit définie comme un chemin sur le système de fichiers de l'ordinateur ou une adresse réseau.
Receiving data from a peer using a socket
Recevoir des données d'un pair en utilisant un socket
The following program binds its socket to a given socket address and then waits for receiving new bytes, using the already created socket ``sock``.
Le programme suivant lie son socket à une adresse de socket donnée et attend ensuite de recevoir de nouveaux octets, en utilisant le socket ``sock`` déjà créé.
Depending on the socket address family, the operating system might implicitly assign an address to an unbound socket upon a call to ``write``, ``send`` or ``sendto``. While this is a useful behavior, describing it precisely is out of the scope of this section.
En fonction de la famille d'adresse de la socket, le système d'exploitation peut implicitement assigner une adresse à un socket non liée lors d'un appel à ``write``, ``send`` ou ``sendto``. Bien que ce comportement soit utile, sa description précise n'entre pas dans le cadre de cette section.
Using this code, the program will read and print an arbitrary message received from an arbitrary peer who knows the program's socket address. If we want to know the address of the peer that sent us the message, we can use the ``recvfrom`` system call. This is what a modified version of ``bind_and_receive_from_peer`` is doing below.
En utilisant ce code, le programme lira et imprimera un message arbitraire reçu d'un homologue arbitraire qui connaît l'adresse de socket du programme. Si nous voulons connaître l'adresse de l'homologue qui nous a envoyé le message, nous pouvons utiliser l'appel système ``recvfrom``. C'est ce que fait une version modifiée de ``bind_and_receive_from_peer`` ci-dessous.
This function is now using the ``recvfrom`` system call that will also provide the address of the peer who sent the message. As addresses are generic and can have different sizes, ``recvfrom`` also tells us the size of the address that it has written.
Cette fonction utilise maintenant l'appel système ``recvfrom`` qui fournira également l'adresse du pair qui a envoyé le message. Comme les adresses sont génériques et peuvent avoir différentes tailles, ``recvfrom`` nous indique également la taille de l'adresse qu'il a écrite.
``connect``: connecting a socket to a remote address
``connect``:connecter un socket à une adresse distante
This system call will assign the socket ``sockfd`` to the ``addr`` remote socket address. The process can then use the ``send`` and ``write`` system calls that do not to specify the destination socket address. Furthermore, the calls to ``recv`` and ``read`` will only deliver messages sent by this remote address. This is useful when we only care about the other peer messages.
Cet appel système va assigner la socket ``sockfd`` à l'adresse de socket distante ``addr``. Le processus peut alors utiliser les appels système ``send`` et ``write`` qui ne spécifient pas l'adresse de la socket de destination. De plus, les appels à ``recv`` et ``read`` ne délivreront que les messages envoyés par cette adresse distante. Ceci est utile lorsque l'on ne s'intéresse qu'aux messages des autres pairs.
The following program connects a socket to a remote address, sends a message and waits for a reply.
Le programme suivant connecte un socket à une adresse distante, envoie un message et attend une réponse.
Creating a new socket to communicate through a network
Création d'une nouvelle socket pour communiquer via un réseau
Until now, we learned how to use sockets that were already created. When writing a whole program, you will have to create you own sockets and choose the concrete technology that it will use to communicate with others. In this section, we will create new sockets and allow a program to communicate with processes located on another computer using a network. The most recent standardized technology used to communicate through a network is the :term:`IPv6` network protocol. In the IPv6 protocol, hosts are identified using *IPv6 addresses*. Modern operating systems allow IPv6 network communications between programs to be done using the socket API, just as we did in the previous sections.
Jusqu'à présent, nous avons appris à utiliser des sockets déjà créés. Lorsque vous écrirez un programme complet, vous devrez créer vos propres sockets et choisir la technologie concrète qu'il utilisera pour communiquer avec les autres. Dans cette section, nous allons créer de nouveaux sockets et permettre à un programme de communiquer avec des processus situés sur un autre ordinateur en utilisant un réseau. La technologie standardisée la plus récente utilisée pour communiquer via un réseau est le protocole réseau :term:`IPv6`. Dans le protocole IPv6, les hôtes sont identifiés à l'aide d'adresses *IPv6*. Les systèmes d'exploitation modernes permettent d'établir des communications réseau IPv6 entre programmes à l'aide de l'API socket, comme nous l'avons fait dans les sections précédentes.
A program can use the ``socket`` system call to create a new socket.
Un programme peut utiliser l'appel système ``socket`` pour créer un nouveau socket.
The ``domain`` parameter specifies the address family that we will use to concretely perform the communication. For an IPv6 socket, the ``domain`` parameter will be set to the value ``AF_INET6``, telling the operating system that we plan to communicate using IPv6 addresses. The ``type`` parameter specifies the communication guarantees that we need. For now, we will use the type ``SOCK_DGRAM`` which allows us to send *unreliable messages*. This means that each data that we send at each call of ``sendto`` will either be completely received or not received at all. The last parameter will be set to ``0``. The following line creates a socket, telling the operating system that we want to communicate using IPv6 addresses and that we want to send unreliable messages.
Le paramètre ``domain`` spécifie la famille d'adresses que nous allons utiliser pour réaliser concrètement la communication. Pour une socket IPv6, le paramètre ``domain`` prendra la valeur ``AF_INET6``, indiquant au système d'exploitation que nous prévoyons de communiquer en utilisant des adresses IPv6. Le paramètre ``type`` spécifie les garanties de communication dont nous avons besoin. Pour l'instant, nous allons utiliser le type ``SOCK_DGRAM`` qui nous permet d'envoyer des *messages non fiables*. Cela signifie que chaque donnée que nous envoyons à chaque appel de ``sendto`` sera soit complètement reçue, soit pas du tout reçue. Le dernier paramètre sera fixé à "0". La ligne suivante crée un socket, indiquant au système d'exploitation que nous voulons communiquer en utilisant des adresses IPv6 et que nous voulons envoyer des messages non fiables.
Sending a message to a remote peer using its IPv6 address
Envoi d'un message à un homologue distant en utilisant son adresse IPv6
Now that we created an IPv6 socket, we can use it to reach another program if we know its IPv6 address. IPv6 addresses have a human-readable format that can be represented as a string of characters. The details of IPv6 addresses are out of scope of this section but here are some examples :
Maintenant que nous avons créé un socket IPv6, nous pouvons l'utiliser pour atteindre un autre programme si nous connaissons son adresse IPv6. Les adresses IPv6 ont un format lisible par l'homme qui peut être représenté par une chaîne de caractères. Les détails des adresses IPv6 sont hors de portée de cette section mais voici quelques exemples :
The ``::1`` IPv6 address identifies the computer on which the current program is running.
L'adresse IPv6 ``::1`` identifie l'ordinateur sur lequel le programme tourne.
The ``2001:6a8:308f:9:0:82ff:fe68:e520`` IPv6 address identifies the computer serving the ``https://beta.computer-networking.info`` website.
L'adresse IPv6 ``2001:6a8:308f:9:0:82ff:fe68:e520`` identifie l'ordinateur qui dessert le site ``https://beta.computer-networking.info``.
An IPv6 address often identifies a computer and not a program running on the computer. In order to identify a specific program running on a specific computer, we use a *port number* in addition to the IPv6 address. A program using an IPv6 socket is this identified using :
Une adresse IPv6 identifie souvent un ordinateur et non pas un programme exécuté sur cet ordinateur. Afin d'identifier un programme spécifique fonctionnant sur un ordinateur spécifique, nous utilisons un *numéro de port* en plus de l'adresse IPv6. Un programme utilisant un socket IPv6 est ainsi identifié par :
The IPv6 address of the computer
L'adresse IPv6 de l'ordinateur
The port number identifying the program running on the computer
Le port du programme qui tourne sur l'ordinateur
A program can use the ``struct sockaddr_in6`` to represent IPv6 socket addresses. The following program creates a ``struct sockaddr_in6`` that identifies the program that reserved the port number ``55555`` on the computer identified by the ``::1`` IPv6 address.
Un programme peut utiliser la structure sockaddr_in6 pour représenter les adresses de socket IPv6. Le programme suivant crée une ``struct sockaddr_in6`` qui identifie le programme ayant réservé le numéro de port ``55555`` sur l'ordinateur identifié par l'adresse IPv6 ``::1``.
Now, we have built everything we need to send a message to the remote program. The ``create_socket_and_send_message`` function below assembles all the building blocks we created until now in order to send the message ``"hello"`` to the program running on port ``55555`` on the computer identified by the ``::1`` IPv6 address.
Maintenant, nous avons construit tout ce dont nous avons besoin pour envoyer un message au programme distant. La fonction ``create_socket_and_send_message`` ci-dessous assemble toutes les briques que nous avons créées jusqu'à présent afin d'envoyer le message ``hello"`` au programme tournant sur le port ``55555`` sur l'ordinateur identifié par l'adresse IPv6 ``::1``.
Note that we can reuse our ``send_hello_to_peer`` function without any modification as we wrote it to handle any kind of sockets, including sockets using the IPv6 network protocol.
Notez que nous pouvons réutiliser notre fonction ``send_hello_to_peer`` sans aucune modification puisque nous l'avons écrite pour gérer tout type de sockets, y compris les sockets utilisant le protocole réseau IPv6.
Endianness: exchanging integers between different computers
Endianness : échange d'entiers entre différents ordinateurs
Besides character strings, some applications also need to exchange 16 bits and 32 bits fields such as integers. A naive solution would have been to send the 16- or 32-bits field as it is encoded in the host's memory. Unfortunately, there are different methods to store 16- or 32-bits fields in memory. Some CPUs store the most significant byte of a 16-bits field in the first address of the field while others store the least significant byte at this location. When networked applications running on different CPUs exchange 16 bits fields, there are two possibilities to transfer them over the transport service :
Outre les chaînes de caractères, certaines applications doivent également échanger des champs de 16 et 32 bits tels que des entiers. Une solution naïve aurait été d'envoyer le champ de 16 ou 32 bits tel qu'il est codé dans la mémoire de l'hôte. Malheureusement, il existe différentes méthodes pour stocker les champs de 16 ou 32 bits en mémoire. Certains processeurs stockent l'octet le plus significatif d'un champ de 16 bits dans la première adresse du champ, tandis que d'autres stockent l'octet le moins significatif à cet endroit. Lorsque des applications en réseau fonctionnant sur des unités centrales différentes échangent des champs de 16 bits, il existe deux possibilités pour les transférer via le service de transport :
send the most significant byte followed by the least significant byte
envoyer l'octet le plus significatif suivi de l'octet le moins significatif
send the least significant byte followed by the most significant byte
envoyer l'octet le moins significatif suivi de l'octet le plus significatif
Besides 16 and 32 bit words, some applications need to exchange data structures containing bit fields of various lengths. For example, a message may be composed of a 16 bits field followed by eight, one bit flags, a 24 bits field and two 8 bits bytes. Internet protocol specifications will define such a message by using a representation such as the one below. In this representation, each line corresponds to 32 bits and the vertical lines are used to delineate fields. The numbers above the lines indicate the bit positions in the 32-bits word, with the high order bit at position `0`.
Outre les mots de 16 et 32 bits, certaines applications doivent échanger des structures de données contenant des champs de bits de différentes longueurs. Par exemple, un message peut être composé d'un champ de 16 bits suivi de huit drapeaux d'un bit, d'un champ de 24 bits et de deux octets de 8 bits. Les spécifications du protocole Internet définiront un tel message en utilisant une représentation telle que celle qui suit. Dans cette représentation, chaque ligne correspond à 32 bits et les lignes verticales sont utilisées pour délimiter les champs. Les chiffres au-dessus des lignes indiquent la position des bits dans le mot de 32 bits, le bit de poids fort étant en position "0".
Message format
Format du message
The message mentioned above will be transmitted starting from the upper 32-bits word in network byte order. The first field is encoded in 16 bits. It is followed by eight one bit flags (`A-H`), a 24 bits field whose high order byte is shown in the first line and the two low order bytes appear in the second line followed by two one byte fields. This ASCII representation is frequently used when defining binary protocols. We will use it for all the binary protocols that are discussed in this book.
Le message mentionné ci-dessus sera transmis en commençant par le mot supérieur de 32 bits dans l'ordre des octets du réseau. Le premier champ est codé sur 16 bits. Il est suivi de huit drapeaux d'un bit (`A-H`), d'un champ de 24 bits dont l'octet de poids fort figure sur la première ligne et les deux octets de poids faible apparaissent sur la deuxième ligne, suivis de deux champs d'un octet. Cette représentation ASCII est fréquemment utilisée lors de la définition de protocoles binaires. Nous l'utiliserons pour tous les protocoles binaires abordés dans ce livre.
Exercises
Exercices
Here are some exercises that will help you to learn how to use sockets.
Voici quelques exercices qui vous permettront d'apprendre à utiliser les sockets.
Footnotes
Notes de pied de page
For example, the ``htonl(3)`` (resp. ``ntohl(3)``) function the standard C library converts a 32-bits unsigned integer from the byte order used by the CPU to the network byte order (resp. from the network byte order to the CPU byte order). Similar functions exist in other programming languages.
Par exemple, la fonction ``htonl(3)`` (resp. ``ntohl(3)``) de la bibliothèque C standard convertit un entier non signé de 32 bits de l'ordre des octets utilisé par le CPU à l'ordre des octets du réseau (resp. de l'ordre des octets du réseau à l'ordre des octets du CPU). Des fonctions similaires existent dans d'autres langages de programmation.