Mode non connecte en C++

Mode non connecte en C++

1 – Schéma d’une relation client-serveur

Voici le schéma d’une relation mode non-connecté entre un client et un serveur.

c-mode-non-connecte schema relation client serveur

2 – Initialisation de la socket

Une socket est un mécanisme de couche 5 permettant une communication réseau inter application. En résumé, elle permet la communication entre deux applications situés sur deux machines distinctes.

2.1 Commande WSAStartup()

Windows se basant sur Winsock, nous devons initialiser cette API contrairement à Linux. Pour cela, nous allons utiliser la fonction WSAStartup(). L’utilisation de cette fonction est donc obligatoire pour un système d’exploitation Windows. Si vous utilisez un autre OS, alors passez directement à l’étape suivante. Voici un exemple d’utilisation pour le côté client et serveur. 

// ********************************************************
// Déclaration des variables
// ********************************************************
WSADATA initialisation_win32; // Variable permettant de récupérer la structure d'information sur l'initialisation
int erreur; // Variable permettant de récupérer la valeur de retour des fonctions utilisées

// ********************************************************
// Initialisation de Winsock
// ********************************************************
erreur=WSAStartup(MAKEWORD(2,2),&initialisation_win32);
if (erreur!=0)
      printf("\nDesole, je ne peux pas initialiser Winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
else
      printf("\nWSAStartup  : OK");
  • Le premier paramètre indique la version de Winsock que nous demandons.
  • Le second paramètre est l’adresse d’une structure de type WSADATA qui contient toutes les informations nécessaires résultant de l’initialisation.
  • La fonction devra renvoyer 0 pour indiquer que sont exécution c’est correctement déroulée.

2.2 – Commande socket()

La création d’une socket est obligatoire afin d’obtenir un identifiant unique. Cela permettra aux prochaines fonctions de toutes ce référencer au même id, donc à la même socket. Voici un exemple d’utilisation pour le côté client et serveur.

// ********************************************************
// Déclaration des variables
// ********************************************************
SOCKET id_de_la_socket; // Identifiant de la socket

// ********************************************************
// Ouverture d'une Socket
// ********************************************************
id_de_la_socket=socket(AF_INET,SOCK_DGRAM,0);
if (id_de_la_socket==INVALID_SOCKET)
      printf("\nDesole, je ne peux pas creer la socket du a l'erreur : %d",WSAGetLastError());
else
      printf("\nsocket      : OK");
  • Le premier paramètre indique que vous désirez crée une socket basé sur le protocole IPv4.
  • Le second paramètre sélectionne le mode connecté via UDP, cela nécessitera alors une ouverture de session.
  • La fonction  retournera l’identifiant de la socket si elle s’est exécutée correctement. Sinon, elle indiquera la valeur INVALID_SOCKET.

3 – Ecoute du port UDP

3.1 – Commande bind()

La commande bind permet de lier la socket à un port et adresse IP d’écoute. Voici un exemple d’utilisation pour le côté serveur.

// ********************************************************
// Déclaration des variables
// ********************************************************
SOCKADDR_IN information_sur_la_source; // Déclaration de la structure des informations lié à l'écoute

// ********************************************************
// Lie la socket à une ip et un port d'écoute
// ********************************************************
information_sur_la_source.sin_family=AF_INET;
information_sur_la_source.sin_addr.s_addr=INADDR_ANY; // Ecoute sur toutes les IP locales  
information_sur_la_source.sin_port=htons(33333); // Ecoute sur le port 33333
erreur=bind(id_de_la_socket,(struct sockaddr*)&information_sur_la_source,sizeof(information_sur_la_source));
if (erreur!=0)
      printf("\nDesole, je ne peux pas ecouter ce port : %d %d",erreur,WSAGetLastError());
else
      printf("\nbind        : OK");
  • Le premier argument est l’identifiant de la socket que vous avez récupéré grâce à la fonction socket().
  • Le second argument est la structure comprenant les informations sur le port et l’adresse IP à se lier.
  • Le troisième paramètre spécifie uniquement la taille de la structure du second.
  • La fonction devra renvoyer 0 pour indiquer que sont exécution c’est correctement déroulée.

4 – Echange des données

4.1 – Commande sendto()

La commande sendto permet l’envoi d’une chaîne de caractère à destination du serveur précisé comme argument de la fonction. Voici un exemple d’utilisation pour le côté client.

// ********************************************************
// Déclaration des variables
// ********************************************************
int nombre_de_caractere; // Indique le nombre de caractères qui a été reçu ou envoyé
char buffer[65535]; // Tampon contenant les données reçues ou envoyées

// ********************************************************
// Envoi des données
// ********************************************************
information_sur_la_destination.sin_family=AF_INET; // Indiquez l'utilisation d'IPV4
information_sur_la_destination.sin_addr.s_addr=inet_addr("10.10.10.10"); // Indiquez l'adresse IP de votre serveur
information_sur_la_destination.sin_port=htons(33333); // Port TCP 33333 à destination du serveur
strcpy(buffer,"Coucou, je suis les donnees. www.frameip.com"); // Copie la chaine de caractère dans buffer
nombre_de_caractere=sendto(id_de_la_socket,buffer,strlen(buffer),0,(struct sockaddr*)&information_sur_la_destination,sizeof(information_sur_la_destination));
if (nombre_de_caractere==SOCKET_ERROR)
      printf("\nDesole, je ne peux pas envoyer les donnees du a l'erreur : %d",WSAGetLastError());
else
      printf("\nsend : OK");
  • Le premier argument est l’identifiant de la socket que vous avez récupéré grâce à la fonction socket().
  • Le second argument est le buffer contenant les données à envoyer.
  • Le troisième paramètre spécifie uniquement la taille du buffer.
  • Le quatrième fait référence à la structure comprenant les informations sur le port et l’adresse IP de destination.
  • Le cinquième indique la taille de la structure précédente.
  • La fonction renverra le nombre de caractère qui a été émis.

4.2 – Commande recvfrom()

La commande recvfrom permet de recueillir dans un buffer les données reçues sur une socket. Voici un exemple d’utilisation pour le côté serveur.

// ********************************************************
// Reception des données
// ********************************************************
tempo=sizeof(information_sur_la_source); // Passe par une variable afin d'utiliser un pointeur
nombre_de_caractere=recvfrom(id_de_la_socket,buffer,1515,0,(struct sockaddr*)&information_sur_la_source,&tempo);
buffer[nombre_de_caractere]=0; // Permet de fermer le tableau après le contenu des data, car la fonction recvfrom ne le fait pas
printf("\nVoici les donnees : %s",buffer);
  • Le premier argument est l’identifiant de la socket que vous avez récupéré grâce à la fonction accept().
  • Le second argument est le buffer contenant les données à recevoir.
  • Le troisième paramètre représente le nombre maximum de caractère attendu.
  • Le quatrième fait référence à la structure comprenant les informations sur le port et l’adresse IP en écoute.
  • Le cinquième indique la taille de la structure précédente.
  • La fonction renverra le nombre de caractère qui a été reçu.

5 – Libération de la socket

5.1 Commande closesocket()

Cette fonction permet de libérer proprement l’accès à la socket. Durement conseillé pour le respect d’un développement propre et d’une utilisation saine du système d’exploitation. Voici un exemple d’utilisation pour le côté client et serveur.

// ********************************************************
// Fermeture de la socket correspondant à la commande socket()
// ********************************************************
erreur=closesocket(id_de_la_socket);
if (erreur!=0)
      printf("\nDesole, je ne peux pas liberer la socket du a l'erreur : %d %d",erreur,WSAGetLastError());
else
      printf("\nclosesocket : OK");
  • Le premier argument est l’identifiant de la socket que vous avez récupéré grâce à la fonction socket().
  • La fonction devra renvoyer 0 pour indiquer que sont exécution c’est correctement déroulée.

5.2 Commande WSACleanup()

Cette étape n’est utile et fonctionnelle que dans le cadres du système d’exploitation Microsoft. Cette fonction permet de libérer l’accès à Winsock. Attention, dans un environnement multiprocess, l’utilisation de cette commande fermera les accès de tous les process. Voici un exemple d’utilisation pour le côté client et serveur.

// ********************************************************
// Quitte proprement le winsock ouvert avec la commande WSAStartup
// ********************************************************
erreur=WSACleanup(); // A appeler autant de fois qu'il a été ouvert.
if (erreur!=0)
      printf("\nDesole, je ne peux pas liberer winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
else
      printf("\nWSACleanup  : OK");
  • La fonction devra renvoyer 0 pour indiquer que sont exécution c’est correctement déroulée.

6 – Exemple complet d’une communication client serveur

6.1 – Côté serveur

Voici un exemple de code fonctionnel qui permet d’écouter sur le port UDP 33333 et d’afficher les données reçues. Le fichier cpp est disponible ici.

// ********************************************************
// Les includes
// ********************************************************
#include <winsock2.h> // pour les fonctions socket
#include <cstdio> // Pour les Sprintf
 
// ********************************************************
// Les librairies
// ********************************************************
#pragma comment(lib,"ws2_32.lib")
 
// ********************************************************
// Définition des variables
// ********************************************************
WSADATA initialisation_win32; // Variable permettant de récupérer la structure d'information sur l'initialisation
int erreur; // Variable permettant de récupérer la valeur de retour des fonctions utilisées
int tempo; // Variable temporaire de type int
int nombre_de_caractere; // Indique le nombre de caractères qui a été reçu ou envoyé
char buffer[65535]; // Tampon contenant les données reçues ou envoyées
SOCKET id_de_la_socket; // Identifiant de la socket
SOCKADDR_IN information_sur_la_source; // Déclaration de la structure des informations lié à l'écoute
 
int main (int argc, char* argv[])
      {
      printf("\nBonjour, vous etes du cote serveur. www.frameip.com\n");
 
      // ********************************************************
      // Initialisation de Winsock
      // ********************************************************
      erreur=WSAStartup(MAKEWORD(2,2),&initialisation_win32);
      if (erreur!=0)
            printf("\nDesole, je ne peux pas initialiser Winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nWSAStartup  : OK");
 
      // ********************************************************
      // Ouverture d'une Socket
      // ********************************************************
      id_de_la_socket=socket(AF_INET,SOCK_DGRAM,0);
      if (id_de_la_socket==INVALID_SOCKET)
            printf("\nDesole, je ne peux pas creer la socket du a l'erreur : %d",WSAGetLastError());
      else
            printf("\nsocket      : OK");
 
      // ********************************************************
      // Lie la socket à une ip et un port d'écoute
      // ********************************************************
      information_sur_la_source.sin_family=AF_INET;
      information_sur_la_source.sin_addr.s_addr=INADDR_ANY; // Ecoute sur toutes les IP locales  
      information_sur_la_source.sin_port=htons(33333); // Ecoute sur le port 33333
      erreur=bind(id_de_la_socket,(struct sockaddr*)&information_sur_la_source,sizeof(information_sur_la_source));
      if (erreur!=0)
            printf("\nDesole, je ne peux pas ecouter ce port : %d %d",erreur,WSAGetLastError());
      else
            printf("\nbind        : OK");
 
      // ********************************************************
      // Reception des données
      // ********************************************************
      tempo=sizeof(information_sur_la_source); // Passe par une variable afin d'utiliser un pointeur
      nombre_de_caractere=recvfrom(id_de_la_socket,buffer,1515,0,(struct sockaddr*)&information_sur_la_source,&tempo);
      buffer[nombre_de_caractere]=0; // Permet de fermer le tableau après le contenu des data, car la fonction recvfrom ne le fait pas
      printf("\nVoici les donnees : %s",buffer);
 
      // ********************************************************
      // Fermeture de la socket correspondante à la commande socket()
      // ********************************************************
      erreur=closesocket(id_de_la_socket);
      if (erreur!=0)
            printf("\nDesole, je ne peux pas liberer la socket du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nclosesocket : OK");
 
      // ********************************************************
      // Quitte proprement le winsock ouvert avec la commande WSAStartup
      // ********************************************************
      erreur=WSACleanup(); // A appeler autant de fois qu'il a été ouvert.
      if (erreur!=0)
            printf("\nDesole, je ne peux pas liberer winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nWSACleanup  : OK");
      }

6.2 – Côté client

Voici un exemple de code fonctionnel qui permet d’envoyer des données sur le port UDP 33333, n’oubliez pas de changer l’IP de destination pour correspondre à votre serveur. Le fichier cpp est disponible ici.

// ********************************************************
// Les includes
// ********************************************************
#include <winsock2.h> // pour les fonctions socket
#include <cstdio> // Pour les Sprintf
 
// ********************************************************
// Les librairies
// ********************************************************
#pragma comment(lib,"ws2_32.lib")
 
// ********************************************************
// Définition des variables
// ********************************************************
WSADATA initialisation_win32; // Variable permettant de récupérer la structure d'information sur l'initialisation
int erreur; // Variable permettant de récupérer la valeur de retour des fonctions utilisées
int tempo; // Variable temporaire de type int
int nombre_de_caractere; // Indique le nombre de caractères qui a été reçu ou envoyé
char buffer[65535]; // Tampon contennant les données reçues ou envoyées
SOCKET id_de_la_socket; // Identifiant de la socket
SOCKADDR_IN information_sur_la_destination; // Déclaration de la structure des informations lié au serveur
 
int main (int argc, char* argv[])
      {
      printf("\nBonjour, vous etes du cote client. www.frameip.com\n");
 
      // ********************************************************
      // Initialisation de Winsock
      // ********************************************************
      erreur=WSAStartup(MAKEWORD(2,2),&initialisation_win32);
      if (erreur!=0)
            printf("\nDesole, je ne peux pas initialiser Winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nWSAStartup  : OK");
 
      // ********************************************************
      // Ouverture d'une Socket
      // ********************************************************
      id_de_la_socket=socket(AF_INET,SOCK_DGRAM,0);
      if (id_de_la_socket==INVALID_SOCKET)
            printf("\nDesole, je ne peux pas creer la socket du a l'erreur : %d",WSAGetLastError());
      else
            printf("\nsocket      : OK");
 
      // ********************************************************
      // Envoi des données
      // ********************************************************
      information_sur_la_destination.sin_family=AF_INET; // Indiquez l'utilisation d'IPV4
      information_sur_la_destination.sin_addr.s_addr=inet_addr("10.10.10.10"); // Indiquez l'adresse IP de votre serveur
      information_sur_la_destination.sin_port=htons(33333); // Port TCP 33333 à destination du serveur
      strcpy(buffer,"Coucou, je suis les donnees. www.frameip.com"); // Copie la chaine de caractère dans buffer
      nombre_de_caractere=sendto(id_de_la_socket,buffer,strlen(buffer),0,(struct sockaddr*)&information_sur_la_destination,sizeof(information_sur_la_destination));
      if (nombre_de_caractere==SOCKET_ERROR)
            printf("\nDesole, je ne peux pas envoyer les donnees du a l'erreur : %d",WSAGetLastError());
      else
            printf("\nsend        : OK");
 
      // ********************************************************
      // Fermeture de la socket correspondant à la commande socket()
      // ********************************************************
      erreur=closesocket(id_de_la_socket);
      if (erreur!=0)
            printf("\nDesole, je ne peux pas liberer la socket du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nclosesocket : OK");
 
      // ********************************************************
      // Quitte proprement le winsock ouvert avec la commande WSAStartup
      // ********************************************************
      erreur=WSACleanup(); // A appeler autant de fois qu'il a été ouvert.
      if (erreur!=0)
            printf("\nDesole, je ne peux pas liberer winsock du a l'erreur : %d %d",erreur,WSAGetLastError());
      else
            printf("\nWSACleanup  : OK");
      }

8 – Suivi du document

Création et suivi de la documentation par _SebF

9 – Discussion autour du mode non connecte en C++

Vous pouvez poser toutes vos questions, faire part de vos remarques et partager vos expériences à propos du mode non connecte en C++. Pour cela, n’hésitez pas à laisser un commentaire ci-dessous :

X - Commentaire et discussion

Laisser un commentaire

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