Intro :
Pour concevoir un jeu vidéo multijoueur il vous faut utiliser dans le programme C++ des objets qu’on appelle sockets. Ceci afin de pouvoir faire communiquer des ordinateurs à distance.
Prérequis :
– savoir un peu lire et écrire du C++
– être sous Windows.
Explications :
Les sockets sont des objets représentants une interface de connexion liée à une machine afin de faire communiquer les ordinateurs à travers un réseau (a fortiori par Internet)
1 – Tout d’abord je vais vous présenter un petit programme qui affiche une page HTML dans la console à partir d’une URL web en utilisant les sockets. Rien ne vaut un bel exemple concret pour comprendre !
2 – Aussi, comme il est important de connaître de quoi l’on parle, je vais énumérer toutes les fonctions et mots-clés utilisés dans la programmation socket en C.
1 – Voici ce premier programme :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
/** Inspirez-vous des commentaires pour comprendre **/ #include <winsock2.h> #include <stdio.h> #include <stdlib.h> // Fonction qui permet d'afficher le contenu d'une page web void FetchAndPrintPage(SOCKET socket, const char* sHost, const char* sPage) { // Chaîne de caractère représentant la requête HTTP const char* sRequestFormat = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: FetchAndPrint.c\r\n\r\n"; char* sMsg = new char[4096]; // On compose les paramètres dans une chaîne de caractères finale // représentant la requête HTTP à envoyer sprintf(sMsg, sRequestFormat, sPage, sHost); // La fonction send() permet d'envoyer des informations vers // l'ordinateur distant (le serveur) ; ici on envoit la requête HTTP if (send(socket, sMsg, strlen(sMsg), 0) != SOCKET_ERROR) { // Comme la fonction recv(), qui suit, ne réceptionne qu'une partie du message // reçu, il faut l'invoquer en boucle jusqu'à ce que le message // est entièrement réceptionné. while (true) { // Le nombre d'octet reçu int iBytes; // Le tampon de caractères stockant le contenu de la page web char sBuffer[4096]; // La fonction recv() ne réceptionne qu'une partie du message // reçu ; elle retourne le nombre d'octets reçu et stocke ce contenu // dans la variable tampon sBuffer iBytes = recv(socket, sBuffer, 4096, 0); // On sort de la boucle s'il n'y a plus de message à receptionner ou // qu'une erreur a eu lieu ! if (iBytes == 0 && iBytes == -1) { break; } // Pour finaliser la chaîne on doit rajouter le caractère nul à celle-ci sBuffer[iBytes-1] = '\0'; // On affiche le contenu de la page web dans la console printf("\n\n%s\n", sBuffer); // On met en pause le programme afin de lire la portion du texte reçu system("pause"); } } else { printf("Socket Error Code = %i\n", WSAGetLastError()); } delete sMsg; } int main() { // Ces deux suivantes instructions initialisent // l'utilisation des sockets sous Windows WSADATA WSAData; WSAStartup(MAKEWORD(2,2), &WSAData); // Structure utilisée afin de stocker les informations d'un socket in_addr addr; const char* sHost = "anthroponaute.fr"; // Cette fonction créé un socket SOCKET s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { // Affiche la signification de l'erreur printf("Socket Error Code = %i\n", WSAGetLastError()); system("pause"); exit(EXIT_FAILURE); } // Pour obtenir des informations socket sur cette adresse web hostent* pHost = gethostbyname(sHost); if (pHost != nullptr) { printf("Adress Name = %s\n", pHost->h_name); addr.s_addr = *(u_long*) pHost->h_addr_list[0]; printf("IP Address = %s\n", inet_ntoa(addr)); } else { printf("Socket Error Code = %i", WSAGetLastError()); system("pause"); exit(EXIT_FAILURE); } // On remplit cette structure afin de configurer et situer notre socket SOCKADDR_IN adress_in; adress_in.sin_family = AF_INET; adress_in.sin_addr = addr; adress_in.sin_port = htons(80); /*************/ // On se connecte sur le serveur web if (connect(s, (SOCKADDR*)&adress_in, sizeof(adress_in)) == SOCKET_ERROR) { printf("Socket Error Code = %i\n", WSAGetLastError()); system("pause"); exit(EXIT_FAILURE); } else { printf("Connected !\n"); } // On affiche la page du site en spécifiant son répertoire FetchAndPrintPage(s, sHost, "/blog-informatique/"); WSACleanup(); return 0; } |
Copiez / collez et compilez !
2 – Voici maintenant tous les principes, concepts et fonctions de la programmation de sockets :
Voici les fichiers d’en-tête nécessaires :
|
1 2 3 |
#include <winsock2.h> #include <stdio.h> #include <stdlib.h> |
Voici les #define préétablis :
|
1 2 3 4 5 6 |
SOCKET pour l'utilisation de socket (à la place de int) SOCKADDR_IN pour struct sockaddr_in SOCKADDR pour struct sockaddr IN_ADDR pour struct in_addr INVALID_SOCKET -1 SOCKET_ERROR -1 |
Voici la structure principale à utiliser :
|
1 2 3 4 5 6 7 |
struct sockaddr_in { short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; |
Elle est utilisée pour configurer la socket.
Voici comment l’utiliser :
|
1 2 3 4 |
SOCKADDR_IN sin; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_family = AF_INET; sin.sin_port = htons(23); |
– sin.sin_addr.s_addr : c’est l’adresse qu’on affecte à cette configuration de socket (c’est peut-être l’adresse représentant un client ou un serveur, cela dépend du rôle de notre socket)
– INADDR_ANY : adresse IP automatique
Pour spécifier une adresse manuellement on peut utiliser la fonction inet_addr()
|
1 |
sin.sin_addr.s_addr = inet_addr("127.0.0.1"); |
– sin.sin_family = AF_INET : spécifie que l’on passe la socket en mode Internet
– sin.sin_port : spécifie le numéro de port de la socket à utiliser
En occurrence, qu’est-ce qu’un port ?
Un port sert à spécifier à quel service d’application les informations doivent être envoyées.
En effet sur le réseau Internet global, chaque ordinateur est représenté par un numéro d’IP, mais cette adresse IP ne spécifie pas à quelle application de l’ordinateur l’information doit être envoyé.
En conséquence les applications / services sont identifiés par des numéros de port (ex : port 80 pour
le service HTTP)
Ces deux fonctions doivent être appelées pour initialiser ou clôturer l’utilisation des sockets :
|
1 2 3 4 5 6 |
// Au début du programme : WSADATA WSAData; WSAStartup(MAKEWORD(2,2), &WSAData); // A la fin du programme : WSACleanup(); |
Pour initialiser une socket :
|
1 |
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); |
Voici les différents paramètres de cette fonction :
|
1 |
AF_INET // Spéficie que l'on va utiliser le mode Internet |
Types de socket :
|
1 2 3 |
SOCK_STREAM // Spéficie que l'on va utiliser le protocole TCP/IP. SOCK_DGRAM // Spéficie que l'on va utiliser le protocole UDP/IP. |
– Le protocole TCP est un protocole dit connecté. Il contrôle si le paquet
est arrivé à destination si ce n’est pas le cas il le renvoie.
– Le procotole UDP est un protocole dit non connecté. A l’inverse du protocole TCP
il ne contrôle pas si le paquet est arrivé à destination.
Cette fonction permet de convertir une addresse IP (en char*) afin d’être
affectée à la structure IN_ADDR.
|
1 |
inet_addr("127.0.0.1"); |
|
1 |
inet_ntoa() |
|
1 |
int connect(int socket, struct sockaddr* addr, socklen_t addrlen); |
|
1 |
int bind(int socket, const struct sockaddr* addr, socklen_t addrlen); |
|
1 |
int listen(int socket, int backlog); |
|
1 |
int accept(int socket, struct sockaddr* addr, socklen_t* addrlen); |
Résumé :
Nous avons créé un programme qui affiche une page web afin de comprendre la programmation sockets.
Au final nous avons énuméré tous les concepts liés à la programmation sockets.
Références :
– http://pub.phyks.me/sdz/sdz/les-sockets.html


