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 :
/** 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 :
#include <winsock2.h> #include <stdio.h> #include <stdlib.h>
Voici les #define préétablis :
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 :
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 :
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()
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 :
// Au début du programme : WSADATA WSAData; WSAStartup(MAKEWORD(2,2), &WSAData); // A la fin du programme : WSACleanup();
Pour initialiser une socket :
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
Voici les différents paramètres de cette fonction :
AF_INET // Spéficie que l'on va utiliser le mode Internet
Types de socket :
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.
inet_addr("127.0.0.1");
inet_ntoa()
int connect(int socket, struct sockaddr* addr, socklen_t addrlen);
int bind(int socket, const struct sockaddr* addr, socklen_t addrlen);
int listen(int socket, int backlog);
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

