Le réseau dans les jeux vidéo

Connexion virtuelle sur UDP

Glenn Fiedler

Traducteur

Bousk

Dans un jeu multijoueur, nous voulons généralement échanger des paquets entre un petit nombre d'ordinateurs connectés entre eux. La première étape sera de mettre en place un système de connexion. Nous débuterons avec un cas simpliste : créer une connexion virtuelle entre deux ordinateurs en utilisant UDP.

Commentez

- Titre : Le réseau dans les jeux vidéo

- Auteur : Glenn Fiedler

- Traducteur : Bousk

- Parution : 20 mai 2015

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2015 Glenn Fiedler. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Internet n'est pas une série de tubes

- III. Aucune connexion directe

- IV. Comment les paquets sont-ils acheminés

- V. Les connexions virtuelles

- VI. ID de protocole

- VII. Détecter les connexions

- VIII. Détecter les déconnexions

- IX. Conclusion

- X. Remerciements

I - Introduction

Bonjour, et bienvenue dans la traduction française du troisième article de la série d'articles « le réseau dans les jeux vidéo » écrits par Glenn Fiedler.

Dans l'article précédent, je vous ai montré comment envoyer et recevoir des paquets en utilisant UDP.

Puisque UDP est non-connecté, un socket UDP peut être utilisé pour échanger des paquets avec plusieurs ordinateurs différents. Mais dans un jeu multijoueur, nous voulons généralement échanger des paquets entre un petit nombre d'ordinateurs connectés entre eux.

La première étape sera de mettre en place un système de connexion. Nous débuterons avec un cas simpliste : créer une connexion virtuelle entre deux ordinateurs en utilisant UDP.

Mais d'abord, nous allons creuser un peu le fonctionnement d'Internet !

II - Internet n'est pas une série de tubes

En 2006, le sénateur Ted Stevens est entré dans l'histoire du Net avec son célèbre discours sur la loi de neutralité du Net :

« The internet is not something that you just dump something on. It's not a big truck. It's a series of tubes. »

« Internet n'est pas quelque chose sur lequel vous pouvez déverser n'importe quoi. Ce n'est pas un gros camion. C'est une série de tuyaux. »

Quand j'ai commencé à utiliser Internet, je pensais comme Ted. Assis face à l'ordinateur du laboratoire de l'Université de Sidney en 1995, je « surfais sur le web » avec ce nouveau truc appelé Netscape Navigator, et je n'avais absolument aucune idée de ce qu'il se passait.

Vous savez, je pensais qu'à chaque connexion à un site web, il y avait une vraie connexion qui s'effectuait, comme une ligne téléphonique. Je me demandais, combien coûte chaque connexion à un nouveau site ? 30 cents ? 1 dollar ? Est-ce que quelqu'un dans l'université allait venir me taper sur l'épaule et me demander de payer ?

Bien sûr, tout ça semble stupide maintenant.

Il n'y a aucun standardiste où que ce soit qui vous connecte directement via une ligne téléphonique physique à l'ordinateur avec lequel vous souhaitez communiquer, encore moins une série de tubes pneumatiques comme le sénateur Stevens vous aurait laissé croire.

III - Aucune connexion directe

En fait, vos données sont envoyées via Internet Protocol (IP), en paquets qui sautent d'un ordinateur à un autre.

Un paquet peut passer à travers de nombreux ordinateurs avant d'atteindre sa destination. Vous ne pouvez pas connaître le nombre d'ordinateurs à l'avance, puisque ça change dynamiquement selon comment le réseau décide de router les paquets. Vous pourriez même envoyer deux paquets A et B à la même adresse, et ils pourraient prendre des routes différentes. Ceci est l'origine de la non-garantie de l'ordre d'arrivée des paquets en UDP.

Sur les systèmes Unix, vous pouvez inspecter la route que les paquets prennent en appelant « traceroute » et en indiquant une adresse IP ou un nom de domaine distant.

Sur Windows, remplacer « traceroute » par « tracert ».

Essayez avec quelques sites comme ceci :

traceroute slashdot.org
traceroute amazon.com
traceroute google.com
traceroute bbc.co.uk
traceroute news.com.au

Regardez les résultats et vous devriez vous convaincre assez rapidement qu'il n'y a aucune connexion directe.

IV - Comment les paquets sont-ils acheminés

Dans le premier article, j'ai présenté une analogie simple pour l'acheminement d'un paquet, en le décrivant comme une note passée d'une personne à une autre à travers une salle pleine.

Bien que cette analogie traduise l'idée de base, elle est bien trop simple. Internet n'est pas un réseau fini d'ordinateurs, c'est un réseau de réseaux. Et bien sûr, nous n'avons pas à juste passer des lettres à travers une petite pièce, nous avons besoin de les envoyer partout dans le monde. Il est clair alors que la meilleure analogie est le service postal !

Quand vous envoyez une lettre à quelqu'un, vous mettez votre lettre dans une boîte aux lettres et vous faites confiance à ce qu'elle soit remise correctement. Ce n'est pas bien important pour vous de savoir comment elle y arrive, tant qu'elle y arrive. Quelqu'un doit remettre physiquement votre lettre à sa destination bien sûr, mais comment est-ce fait ?

Tout d'abord, le facteur ne va surement pas prendre votre lettre et la remettre personnellement ! Il semble que le service postal n'est pas une série de tubes non plus. Le facteur va plutôt remettre votre lettre au bureau de Poste local pour y être traitée.

Si le destinataire de la lettre est local, alors le bureau de Poste va juste le renvoyer, et un autre facteur va la remettre directement. Mais si l'adresse n'est pas locale, alors ça devient intéressant ! Le bureau de Poste local n'est pas capable de remettre votre lettre directement, donc il l'envoie au niveau « supérieur » de sa hiérarchie, peut-être un bureau de Poste régional qui s'occupe des villes proches, ou peut-être à un centre de courrier d'un aéroport, si l'adresse est lointaine. Dans l'idéal, le transport à proprement parler de la lettre est fait avec un gros camion.

Compliquons-nous les choses et admettons que la lettre soit envoyée de Los Angeles à Sydney, en Australie. Le bureau de Poste local reçoit le courrier, et puisqu'il s'agit d'une adresse internationale, l'envoie directement à l'aéroport international de Los Angeles, LAX. La lettre est traitée à nouveau selon son adresse, et prend un vol pour Sydney.

L'avion atterrit à l'aéroport de Sydney où un système postal totalement différent prend le relais. Maintenant le traitement entier commence à s'effectuer à l'envers. La lettre « descend » la hiérarchie, du traitement général au plus spécifique. Depuis le centre de courrier de l'Aéroport de Sydney, elle est renvoyée à un centre régional, qui l'expédie à un bureau de Poste local, puis la lettre devrait être acheminée à son destinataire par un facteur avec un accent rigolo. Ça alors !

Tout comme les bureaux de Poste déterminent comment remettre les lettres par leur adresse, les réseaux transmettent les paquets selon leur adresse IP. Les détails bas-niveau de cet acheminement et le routage des paquets d'un réseau à l'autre est plutôt complexe, mais l'idée de base est que chaque routeur est juste un autre ordinateur, avec une table de routage qui décrit où les paquets doivent aller selon leur adresse de destination, et une route par défaut pour renvoyer les paquets dont la destination n'a aucune correspondance dans la table de routage. Ces tables de routage, et les connexions physiques qu'elles représentent définissent le réseau des réseaux qu'est Internet.

La tâche de configuration de ces tables de routage est le travail des administrateurs réseau, pas des programmeurs comme nous. Mais si vous voulez en savoir plus à leur sujet, alors cet article de ars technica fournit un aperçu fascinant de comment les réseaux échangent les paquets entre eux via peering et relations de transit. Vous pouvez aussi lire plus de détails sur les tables de routage dans cette FAQ linux et sur le Border Gateway Protocol sur Wikipédia, qui découvre automatiquement comment router les paquets entre les réseaux, faisant d'Internet un système distribué capable de router dynamiquement autour des connexions qui ont des problèmes.

V - Les connexions virtuelles

Revenons à nos connexions.

Si vous avez utilisé des sockets TCP alors vous savez que ça ressemble à une connexion, mais puisque TCP est implémenté par-dessus IP, et qu'IP ne fait qu'envoyer des paquets d'un ordinateur à un autre, il résulte que le concept de connexion de TCP doit être une connexion virtuelle.

Si TCP peut créer une connexion virtuelle par-dessus IP, alors nous pouvons faire de même avec UDP. Définissons notre connexion virtuelle comme deux ordinateurs qui échangent des paquets UDP à une vitesse fixe comme 10 paquets par seconde. Tant que les paquets sont échangés, nous considérons que les deux ordinateurs sont virtuellement connectés.

Notre connexion a deux aspects :

- un ordinateur attend qu'un ordinateur se connecte à lui. Nous l'appellerons le serveur ;

- un autre ordinateur se connecte au serveur avec son adresse IP et son port. Nous l'appellerons le client.

Dans notre cas, nous autorisons un unique client à se connecter au serveur. Nous généraliserons notre système de connexion pour supporter plusieurs connexions simultanées dans un prochain article. Aussi, nous présumons que l'adresse IP du serveur est fixe. Nous traiterons du matchmaking et de NAT traversal (punch-through) dans des articles à venir.

VI - ID de protocole

Puisque UDP est non-connecté, notre socket UDP peut recevoir des paquets envoyés depuis n'importe quel ordinateur.

Nous aimerions affiner ça pour que le serveur reçoive directement les paquets envoyés du client, et que le client ne reçoive que les paquets envoyés du serveur. Nous ne pouvons pas juste filtrer les paquets par leur adresse, parce que le serveur ne connaît pas d'avance l'adresse du client. Alors à la place, nous préfixons chaque paquet UDP avec un petit en-tête contenant un identifiant de protocole de 32 bits comme ceci :

- [uint32 protocol id]

- (paquet…)

VII - Détecter les connexions

Nous avons maintenant besoin d'une détection de la connexion.

Bien sûr nous pourrions faire un système de handshake compliqué, impliquant de nombreux échanges de paquets UDP. Peut-être un paquet client « connexion demandée » envoyé au serveur, auquel le serveur répond un paquet « connexion acceptée », ou peut-être un paquet « je suis occupé » serait renvoyé au client si le serveur a déjà un client connecté.

Ou… nous pouvons juste considérer le premier paquet qui arrive contenant l'identifiant de protocole correct pour établir une connexion.

Le client commence à envoyer des paquets au serveur en présumant que la connexion est effectuée, quand le serveur reçoit le premier paquet du client, il note son adresse IP et son port et commence à envoyer ses paquets réponses.

Le client connaît déjà l'adresse et le port du serveur, puisqu'ils sont spécifiés pour la connexion. Donc quand le client reçoit des paquets, il filtre tout ce qui ne vient pas de l'adresse du serveur. De la même manière, lorsque le serveur reçoit le premier paquet du client, il récupère son adresse et son port de « recvfrom », il est donc capable d'ignorer tous les paquets qui ne viennent pas de ce client.

Nous pouvons nous permettre ce raccourci parce que nous avons seulement deux ordinateurs impliqués dans la connexion. Dans les articles suivants, nous étendrons notre système de connexion pour supporter plus de deux ordinateurs dans une topologie client/serveur ou P2P, et nous améliorerons les échanges nécessaires à la connexion pour quelque chose de plus robuste.

Mais pour le moment, pourquoi faire les choses plus compliquées que nécessaire ?

VIII - Détecter les déconnexions

Comment détectons-nous les déconnexions ?

Eh bien, si une connexion est définie comme la réception de paquets, nous pouvons définir la déconnexion comme l'absence de paquets reçus.

Pour détecter quand nous ne recevons pas de paquets, nous devons sauvegarder le nombre de secondes depuis le dernier paquet reçu de l'ordinateur distant. Nous faisons ceci sur chaque ordinateur, serveur et client.

À chaque paquet reçu, nous réinitialisons notre compteur à 0, et à chaque update nous incrémentons notre compteur de la durée écoulée.

Si ce compteur dépasse une valeur comme 10 secondes, la connexion « expire » et nous nous déconnectons.

Ceci permet aussi de gérer le cas d'un second client qui essaye de se connecter à un serveur qui est déjà connecté avec un autre client. Puisque le serveur est déjà connecté, il ignore les paquets venant d'un autre ordinateur que le client connecté, donc le second client ne recevra aucun paquet réponse à ses requêtes et finira par expirer et se déconnecter.

IX - Conclusion

Et voilà tout ce qui est nécessaire pour configurer une connexion virtuelle : une méthode pour établir la connexion, filtrer les paquets qui n'en sont pas issus, et un délai d'expiration pour détecter la déconnexion.

Notre connexion est aussi réelle que n'importe quelle connexion TCP, et le flux régulier de paquets UDP qu'elle produit est un point de départ acceptable pour un jeu d'action multijoueur.

Nous avons aussi détaillé un peu comment Internet route les paquets. Par exemple, nous savons maintenant que les paquets UDP arrivent parfois dans le désordre parce qu'ils suivent des routes différentes à travers IP ! Regardez une carte d'Internet, n'est-ce pas incroyable que vos paquets arrivent tout court ? Si vous voulez en savoir plus, vous pouvez commencer par cet article sur Wikipédia (http://en.wikipedia.org/wiki/Internet_backbone).

Maintenant que vous avez votre connexion virtuelle en UDP, vous pouvez facilement configurer une relation client/serveur pour un jeu à deux joueurs sans utiliser TCP.

Vous pouvez voir une implémentation de ceci dans l'exemple de code source de cet article.

C'est un simple programme client/serveur qui échange 30 paquets par seconde. Vous pouvez lancer le serveur sur n'importe quelle machine, tant que vous avez une adresse IP publique, puisque nous ne supportons pas de NAT traversal jusque-là.

Lancer le client comme ceci :

./Client 205.10.40.50

Et il essayera de se connecter à l'adresse que vous avez indiquée dans la ligne de commande. Par défaut, il tentera de se connecter à 127.0.0.1 si vous n'en indiquez aucune.

Tant qu'un client est connecté, vous pouvez essayer d'en connecter un autre, et vous recevrez une erreur de connexion échouée. Ceci est voulu. Pour le moment, seulement un client peut se connecter à la fois.

Vous pouvez aussi essayer d'arrêter le client ou le serveur pendant qu'ils sont connectés, et vous devriez remarquer qu'après 10 secondes l'ordinateur distant expirera et se déconnectera. Quand le client expire, l'application se ferme. Le serveur, quant à lui, retourne à un état d'attente de connexion d'un client.

X - Remerciements

Cet article est une traduction autorisée de l'article de Glenn Fiedler.

Merci aussi à f-leb pour sa relecture orthographique.