Programmation de sockets en C++

Programmation De Sockets En C



La programmation socket est devenue un sujet important dans le domaine des réseaux informatiques. Il s’agit d’établir une connexion entre deux nœuds, serveur et client pour communiquer entre eux sans aucune interruption. Le serveur agit comme un auditeur dans le canal de communication et écoute le client sur un port spécifique à une adresse IP. D'autre part, le client agit comme communicateur dans le canal de communication. Le client contacte le serveur afin de créer une connexion et prendre contact avec le serveur. Cet article vise à fournir un guide complet et détaillé de la programmation socket en C++, couvrant les bases, présentant des exemples pratiques et fournissant une explication détaillée du code.

Établir le modèle client-serveur

La programmation socket est le processus qui crée un canal de communication entre le serveur et le client à l'aide de sockets. Dans l'exemple de code suivant, le client démarre un contact avec le serveur et le serveur est configuré pour accepter les connexions client. Comprenons les segments de code serveur et client en démontrant leur fonctionnement principal au sein de la communication réseau. Ce qui suit est le code côté serveur. Voyons d'abord le code, puis expliquons-le en détail, point par point.

1. Côté serveur







Le code du côté serveur du modèle est donné ci-dessous. Voyons ce qui se passe dans le code :



#include
#include
#include
#include

en utilisant espace de noms norme ;

#définir le PORT 8080
#définir MAX_BUF_SIZE 1024

int principal ( ) {
int ser_socket, cli_socket ;
structurer sockaddr_in ser_address, cli_address ;
carboniser bouf [ MAX_BUF_SIZE ] = { 0 } ;

si ( ( ser_socket = prise ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
perrreur ( 'Erreur lors de la création du Socket' ) ;
sortie ( EXIT_FAILURE ) ;
}

ser_address. sin_family = OF_INET ;
ser_address. adresse_sin . adresse_s = INADDR_ANY ;
ser_address. péché_port = htons ( PORT ) ;

si ( lier ( be_socket, ( structurer adresse chaussette * ) & ser_adresse, taille de ( adresse_ser ) ) == - 1 ) {
perrreur ( 'Échec de la liaison' ) ;
sortie ( EXIT_FAILURE ) ;
}

si ( écouter ( be_socket, 3 ) == - 1 ) {
perrreur ( 'Échec de l'écoute' ) ;
sortie ( EXIT_FAILURE ) ;
}

cout << 'Serveur en écoute sur le port' << PORT << '... \n ' ;

socklen_t cli_address_len = taille de ( adresse_cli ) ;
si ( ( cli_socket = accepter ( be_socket, ( structurer adresse chaussette * ) & adresse_cli, & cli_address_len ) ) == - 1 ) {
perrreur ( 'Échec de l'acceptation' ) ;
sortie ( EXIT_FAILURE ) ;
}

lire ( cli_socket, buf, MAX_BUF_SIZE ) ;
cout << 'Le message du client est : ' << bouf << fin ;

envoyer ( cli_socket, 'Message du serveur' , strlen ( 'Message du serveur' ) , 0 ) ;

fermer ( cli_socket ) ;
fermer ( ser_socket ) ;

retour 0 ;
}

L'exemple donné est le code côté serveur du programme C++. Ce code fonctionne pour un simple serveur TCP pour écouter les connexions sur un seul port spécifique. Lorsqu'une connexion est créée avec succès, le serveur recevra un message envoyé par le client. Après cela, il l'imprime sur la console et envoie un message de réponse au client. Comprenons chaque ligne de code.



Le programme commence par inclure les bibliothèques : « iostream » pour les définitions d'entrée/sortie standard, « cstring » pour les fonctions de gestion de chaînes, « unistd.h » pour donner accès à l'API du système d'exploitation POSIX et « arpa/inet.h » pour effectuer les opérations Internet. L'instruction « #define PORT 8080 » signifie qu'elle définit le numéro de port 8080 sur lequel le serveur écoutera. Le « #define MAX_BUF_SIZE 1024 » signifie la taille maximale du tampon pour les données entrantes qui est de 1024.





Dans la fonction principale, deux variables sont initialisées, « ser_socket » et « cli_socket », pour représenter respectivement le serveur et le client. Les trois autres variables qui sont « sockaddr_in », « ser_address » et « cli_address » de type « struct » sont initialisées en tant que structures d'adresse pour le serveur et le client. Après cela, un tampon nommé « buf » est initialisé et stocke les données provenant du client.

La fonction socket() dans la condition « if » crée un nouveau socket TCP. AF_INET désigne IPv4, SOCK_STREAM représente le socket TCP fiable et orienté connexion, le dernier argument qui est 0 est donné pour sélectionner le protocole TCP par défaut, INADDR_ANY accepte les connexions sur n'importe quelle adresse IP, et htons (PORT) convertit le numéro de port du ordre des octets de l'hôte à l'ordre des octets du réseau.



Puisque tout est défini correctement, l'étape suivante consiste à configurer le serveur en tant que listeur sur le port donné et à accepter les connexions sur n'importe quelle interface réseau. La socket est donnée avec les informations dans « ser_address » par la méthode bind(). Nous imprimons une erreur et terminons le processus si la liaison échoue. La fonction accept() ouvre un nouveau socket pour la connexion avec le client, tandis que la fonction Listen() demande au serveur d'attendre les connexions entrantes. Si la fonction accept() échoue, le message d'erreur est imprimé et la fonction se terminera.

Ensuite, le serveur lit le message client avec la fonction read() dans le tampon « buf » puis l'imprime sur la console. La fonction send() est utilisée par le serveur pour envoyer un message en réponse au client. Enfin, en utilisant close(), le serveur ferme le socket du client, mettant ainsi fin au programme afin que toutes les connexions soient correctement fermées et qu'il n'y ait aucune probabilité de violation de données.

2. Côté client

Voyons maintenant ce qui se passe dans le modèle client :

#include
#include
#include
#include

#définir le PORT 8080
#définir SERVER_IP '127.0.0.1'

int principal ( ) {
int cli_socket ;
structurer sockaddr_in ser_address ;
const carboniser * message = « Le client vous envoie ses salutations ! » ;

si ( ( cli_socket = prise ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
perrreur ( 'Erreur lors de la création du socket' ) ;
sortie ( EXIT_FAILURE ) ;
}

ser_address. sin_family = OF_INET ;
ser_address. péché_port = htons ( PORT ) ;

si ( inet_pton ( AF_INET, SERVEUR_IP, & ser_address. adresse_sin ) <= 0 ) {
perrreur ( 'Mauvaise adresse' ) ;
sortie ( EXIT_FAILURE ) ;
}

si ( connecter ( cli_socket, ( structurer adresse chaussette * ) & ser_adresse, taille de ( adresse_ser ) ) == - 1 ) {
perrreur ( 'Échec de connexion' ) ;
sortie ( EXIT_FAILURE ) ;
}
envoyer ( cli_socket, message, strlen ( message ) , 0 ) ;

carboniser bouf [ 1024 ] = { 0 } ;
lire ( cli_socket, buf, taille de ( bouf ) ) ;
norme :: cout << « Réponse du serveur : » << bouf << norme :: fin ;

fermer ( cli_socket ) ;
retour 0 ;
}

Voyons chaque ligne de code pour comprendre comment fonctionne le programme.

Les quatre mêmes bibliothèques – iostream, cstring, unistd.h et arpa/inet.h – sont également incluses côté client. Un numéro de port est également défini avec l'adresse IP de l'hôte local 127.0.0.1. Le message qui doit être transmis au serveur est donné. Le client et le serveur doivent établir une connexion en procédant comme suit :

Le 'if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1);' crée un socket pour IPv4 avec un type de flux et le protocole TCP par défaut. perror() imprime les détails de l'erreur si la fonction socket() ne parvient pas à établir une connexion et quitte le programme.

Le «server_address.sin_port = htons(PORT);» définit le numéro de port après la conversion dans l'ordre des octets du réseau. Ensuite, un autre message d'échec, « Adresse incorrecte », est affiché ici et est imprimé s'il y a un problème avec l'adresse. En localisant l'adresse dans le « ser_address », le client se connectera au serveur. Si la connexion échoue, les détails de l'erreur sont imprimés. La fonction send() transférera le message au serveur, en s'assurant qu'il ne contient aucun indicateur.

Pour recevoir et stocker une réponse du serveur, un tampon nommé « buf » de type « char » est initialisé. La fonction read() lit la réponse du serveur dans le tampon. Enfin, la réponse du serveur est imprimée sur la console. Enfin, la connexion est fermée à l'aide de l'instruction close() pour terminer le socket. Voici le résultat du programme :

Conclusion

La programmation socket est une partie importante de la communication réseau en informatique. Il permet le développement d'applications capables de communiquer sur le réseau, offrant ainsi un large éventail de possibilités allant des simples architectures client-serveur aux systèmes distribués structurés. Lorsqu'un socket est créé dans un contexte de programmation, le programme doit configurer ses caractéristiques de point de terminaison telles que les protocoles, TCP ou UDP, et l'adresse réseau comme l'adresse IP et le numéro de port. Ces sockets permettent aux serveurs d'envoyer et de recevoir les données. Cet article montre un exemple pratique du fonctionnement du modèle client-serveur dans la programmation socket.