Introduction à la programmation réseau (Sockets) – partie 1

48405289

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

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *