[win32] classe de controle de winsock mode multithread

Description

Salut !
Bon ben voilà...
tout est dans le titre :
il s'agit d'une classe (HelkSock) qui permet de controler facilement des winsocks, et ceci de manière multi-thread...
L'utilisation est simple, le code un peu plus fouillis...

dans le zip, il y a un autre exemple (que ci-dessous) de l'utilisation de la classe HelkSock, qui utilise QT (donc prévoyez les DLL pour faire fonctionner le .exe)

Source / Exemple :


//Principe de fonctionnement
HelkSock HKS(true); //true pour TCP, false pour UDP

//pour se connecter à un hote distant, à l'adresse IP 125.124.123.122 sur le port 1234 :
HKS.Connecter("125.124.123.122", 1234);

//pour se mettre en écoute (listen), cad en attente avec le port 1234 ouvert :
HKS.Ecouter(1234);

//pour se déconnecter :
HKS.Fermer();

//rien de plus simple, donc... mais tout ça c'est bien beau, cependant c'est inutile...
//en effet le but est évidemment d'envoyer et de recevoir des données...
//pour envoyer c'est encore très simple :
char pcBuffer [200] = "Ceci est mon message";
HKS.Envoyer(pcBuffer, strlen(pcBuffer));

//pour l'instant, pas de problème
//mais qu'en est-il de la réception ?

/* C'est là que l'aspect multi-thread de ma classe intervient...
En effet on définit, si possible avant utilisation de l'objet, des fonctions
qui serviront de récepteurs pour les différents messages que le HelkSock va
pouvoir vouloir envoyer... Je me suis pour cela basé sur le principe de fonctionnement des Winsock Control en VB et du système de slots en QT
Voilà comment ça marche : */
HKS.LierSlot1(SLOT_REQUETE, ConnexionEffectuee);
//où ConnexionEffectuee est une fonction de la forme :
//HELKSLOT Slot (); //HELKSLOT = int
//ou encore
HKS.LierSlot2(SLOT_REQUETE, RequeteDeConnexion);
//où on a RequeteDeConnexion de la forme
//HELKSLOT Slot(const char * pcBuffer, const ulong uLongVal);
//et aussi, le dernier type de Slot :
HKS.LierSlot3(SLOT_PROGRESSION, ProgressionEnvoi);
//avec progressionEnvoi qui prend pour arguments (ulong uOctetsEnvoyes, ulong uOctetsRestants)

//ceci dit, on peut simplifier les choses en utilisant LierTousSlots,
//qui ne lie que les slots non NULLs, et qui permet en une ligne d'initialiser correctement la classe

/* Maintenant observons le comportement :
On a "lié" la fonction
HELKSLOT RecevoirDonnees(const char * pcDonneesRecues, const ulong uLongueurDonnees);
à la classe pour indiquer l'événement Réception de données (cad SLOT_RECEPTION)
Une fois que l'on sera connecté, on recevra immédiatement, et sans rien faire, les données dans cette fonction ! voilà tout l'intérêt de la classe...
De meme, 4 (oui c'est peu) messages d'erreur peuvent parvenir à une fonction Erreur, et on peut connaitre l'état d'avancement d'un envoi grace à un slot relié à l'événement SLOT_PROGRESSION.

Voici donc un exemple de code pour clarifier les choses (enfin j'espère) : */

#include <iostream>
#include <string>
#include <stdlib.h>
#include <conio.h>
#include <ctime>
using namespace std;

#include "helksock1.h"

HELKSLOT RequeteDeConnexion(const char * pcAdresseIP, const ulong uPortDistant);
HELKSLOT RecevoirDonnees(const char * pcDonneesRecues, const ulong uLongueurDonnees);
HELKSLOT ConnexionEffectuee();
HELKSLOT FermetureHSocket();
HELKSLOT ErreurHSocket(const char * pcErreurDescr, const ulong uErrNum);
HELKSLOT EnvoiEnCours(ulong uEnvoyes, ulong uRestants);
HELKSLOT FinEnvoi();

	HelkSock hks;

int main()
{
	srand(time(0));
	cout << "Lancement du HelkSock..." << endl;
	hks.LierTousSlots(RequeteDeConnexion,
					ConnexionEffectuee,
					RecevoirDonnees,
					EnvoiEnCours,
					FinEnvoi,
					FermetureHSocket,
					ErreurHSocket
					);
	///*
	//tout d'abord, on essaie de fonctionner en mode serveur, cad qu'on écoute sur le port 42201
	//grace à un autre programme, on peut se connecter sur ce port et envoyer un message, qui sera reçu
	hks.Ecouter(42200);

	cout << "Si vous voulez arreter, enfoncez la touche ENTREE" << endl
		<< "Sinon vous pouvez envoyer des mots ou attendre d'en recevoir" << endl;
	string sEnvoi;
	
	char pcBuffer [500] = {0};
	cout << "Veuillez enfoncer une touche quelconque pour continuer ou ENTREE pour quitter..." << endl;
	while (getch() != '\r')
	{
		cout << "Veuillez entrer une phrase a envoyer" << endl;
		cin >> sEnvoi;
			//on envoie le message tapé
		if (!hks.Envoyer(sEnvoi.c_str(), sEnvoi.length()))
			cout << "ERREUR lors de l'envoi : un autre envoi semble etre en cours" << endl;
		Sleep(100);
		cout << "Veuillez enfoncer une touche quelconque pour continuer ou ENTREE pour quitter..." << endl;
	}
	
	cout << "On attend un peu..." << endl;
	Sleep(2500); //il me faut le temps de mettre l'autre prog en écoute
	//maintenant on veut se connecter à un hote "distant" (127.0.0.1 = localhost)
	hks.Fermer();
	cout << "Tentative de connexion sur soi-meme au port 42201" << endl;
	if (hks.Connecter("127.0.0.1", 42201))
	{
		char pcBuffer [500] = {0};
		cout << "Veuillez enfoncer une touche quelconque pour continuer ou ENTREE pour quitter..." << endl;
		while (getch() != '\r')
		{
			cout << "Veuillez entrer une phrase a envoyer" << endl;
			cin >> sEnvoi;
			//on envoie le message tapé
			if (!hks.Envoyer(sEnvoi.c_str(), sEnvoi.length()))
				cout << "ERREUR lors de l'envoi : un autre envoi semble etre en cours" << endl;
			Sleep(100);
			cout << "Veuillez enfoncer une touche quelconque pour continuer ou ENTREE pour quitter..." << endl;
		}
	}
	//*/

	return 0;
}

//en cas de requete de connexion, l'utilisateur peut être averti
HELKSLOT RequeteDeConnexion(const char * pcAdresseIP, const ulong uPortDistant)
{
	cout << "Requete de connexion de la part de " << pcAdresseIP << " sur le port " << uPortDistant << endl;
	return true; //si on retourne false, alors il y a déconnexion et on attend encore, sinon on accepte et...
}

//...une fois la connexion effectuée, on prévient l'utilsateur
HELKSLOT ConnexionEffectuee()
{
	cout << "La connexion vient de se faire" << endl;
	if (!BLOQUER_ECOUTE)
		hks.LancerReception();
	return true;
}

//ici on recoit en permanence ce que le pc distant veut nous envoyer
HELKSLOT RecevoirDonnees(const char * pcDonneesRecues, const ulong uLongueurDonnees)
{
	cout << "Recu : " << pcDonneesRecues << endl;
	return true;
}

//cette fonction est appelée dès que la connexion est coupée
HELKSLOT FermetureHSocket()
{
	cout << "La connexion vient d'etre coupee" << endl;
	return true;
}

//gestion des erreurs
HELKSLOT ErreurHSocket(const char * pcErreurDescr, const ulong uErrNum)
{
	cout << "\7Erreur n " << uErrNum << " :\r\n" << pcErreurDescr << endl;
	return true;
}

//on peut afficher la progression d'un envoi plus gros que la taille par défaut du buffer d'envoi (100 octets ici)
HELKSLOT EnvoiEnCours(ulong uEnvoyes, ulong uRestants)
{
	cout << "Progression : " << uEnvoyes << " octets envoyes + " << uRestants << " octets restants" << endl;
	return true;
}

//l'envoi est fini (utile pour des paquets assez gros)
HELKSLOT FinEnvoi()
{
	cout << "L'envoi est terminé" << endl;
	return true;
}

Conclusion :


Il semble rester quelques bugs, notamment celui ci:
- il est possible (parfois seulement) d'envoyer des messages apres deconnexion, ce qui évidemment genere un message d'erreur
Ceci dit, la classe mériterait d'être encore un peu améliorée (cf interactions threads et objet HelkSock...), et la gestion des erreurs devrait être un peu (beaucoup) améliorée

je suis ouvert à toute suggestion, remarque ou demande éventuelle...

a++ et bonne prog à tous
Helkanen

ps: comment peut-on connaitre le port local sur lequel le socket s'est branché (écoute ou connexion) ?

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.