begin process at 2010 09 09 14:27:45
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Réseaux & Internet

 > ENCAPSULATION D'UNE PARTIE DE L'API SOCKET PORTABLE

ENCAPSULATION D'UNE PARTIE DE L'API SOCKET PORTABLE


 Information sur la source

Note :
Aucune note
Catégorie :Réseaux & Internet Niveau :Initié Date de création :13/01/2004 Date de mise à jour :14/01/2004 20:54:51 Vu / téléchargé :3 997 / 111

Auteur : BlackGoddess

Ecrire un message privé
Commentaire sur cette source (11)
Ajouter un commentaire et/ou une note

 Description

voila, ceci est une tentative d'encapsulation dans des classes d'une partie des api sockets.

ce code nécéssite une partie de boost : boost::noncopyable (www.boost.org) qui empeche la copie d'une instance d'un objet.

Source

  • #ifndef sock_h
  • #define sock_h
  • #include <boost/utility.hpp>
  • #include <string>
  • #ifdef WIN32
  • #include <winsock.h>
  • #define wsastart(); {WSADATA WsaData; WSAStartup(MAKEWORD(1,1), &WsaData);}
  • #define wsaclean(); WSACleanup();
  • #pragma comment(lib, "ws2_32.lib")
  • #else
  • #define wsastart();
  • #define wsaclean();
  • #include <unistd.h>
  • #include <sys/socket.h>
  • #include <netinet/in.h>
  • #include <netdb.h>
  • #include <sys/types.h>
  • #define SOCKET_ERROR (-1) /* défini dans winsock.h */
  • typedef int SOCKET;
  • #define INVALID_SOCKET (SOCKET)(~0) /* défini dans winsock.h */
  • #define closesocket close
  • #endif
  • struct socket_error : std::exception {};
  • struct base_sock : boost::noncopyable
  • {
  • struct address
  • {
  • protected:
  • std::string _name;
  • unsigned short _port;
  • public:
  • address(const address & other)
  • : _name(other._name), _port(other._port)
  • {
  • }
  • address(const std::string & __name, const unsigned short & __port)
  • : _name(__name), _port(__port)
  • {
  • }
  • address(const std::string & __name)
  • : _name(__name), _port(0)
  • {
  • }
  • address(const unsigned short & __port)
  • : _port(__port)
  • {
  • }
  • address()
  • : _port(0)
  • {
  • }
  • address & operator = (const address & other)
  • {
  • _name = other._name;
  • _port = other._port;
  • return *this;
  • }
  • void clear()
  • {
  • _name = "";
  • _port = 0;
  • }
  • bool is_empty() const
  • {
  • return _name == "" && _port == 0;
  • }
  • const unsigned short & port() const
  • {
  • return _port;
  • }
  • const std::string & name() const
  • {
  • return _name;
  • }
  • unsigned short & set_port(const unsigned short & port)
  • {
  • return _port = port;
  • }
  • std::string & set_name(const std::string & name)
  • {
  • return _name = name;
  • }
  • bool operator == (const address & other) const
  • {
  • return (_name == other._name) && (_port == other._port);
  • }
  • };
  • protected:
  • address sock_address;
  • SOCKET s;
  • private:
  • void create()
  • {
  • close();
  • s = socket(AF_INET, SOCK_STREAM, 0);
  • if(s == INVALID_SOCKET) throw socket_error();
  • }
  • public:
  • base_sock()
  • : s(INVALID_SOCKET)
  • {
  • wsastart();
  • }
  • virtual ~base_sock()
  • {
  • close();
  • wsaclean();
  • }
  • bool good() const
  • {
  • return s != INVALID_SOCKET;
  • }
  • virtual void write(const std::string & data) const
  • {
  • if(!good()) throw socket_error();
  • if(::send(s, data.data(), (int)data.size(), 0) == SOCKET_ERROR) throw socket_error();
  • }
  • virtual std::string read() const
  • {
  • if(!good()) throw socket_error();
  • // on crée un buf avec la taille max de data qu'il peut recevoir
  • unsigned long cbdata;
  • int cbopt = sizeof(cbdata);
  • if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&(cbdata), &cbopt) == SOCKET_ERROR) throw socket_error();
  • char* data = new char[cbdata];
  • // on recoit
  • int len;
  • if((len = recv(s, data, cbdata, 0)) == SOCKET_ERROR)
  • {
  • delete[] data;
  • throw socket_error();
  • } else {
  • std::string ret(data, len);
  • delete[] data;
  • return ret;
  • }
  • }
  • void close()
  • {
  • if(!good()) return;
  • ::closesocket(s);
  • sock_address.clear();
  • s = INVALID_SOCKET;
  • }
  • void listen(const address & addr)
  • {
  • create();
  • sockaddr_in sain;
  • sain.sin_family = AF_INET;
  • sain.sin_port = ::htons(addr.port());
  • if(addr.name() == "")
  • sain.sin_addr.s_addr = INADDR_ANY;
  • else
  • {
  • hostent* host = ::gethostbyname(addr.name().c_str());
  • if(host == NULL) throw socket_error();
  • memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4);
  • }
  • if(::bind(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error();
  • sock_address = addr;
  • if(::listen(s, SOMAXCONN) == SOCKET_ERROR) throw socket_error();
  • }
  • void accept(const base_sock & listen_sock)
  • {
  • if(!listen_sock.good()) throw socket_error();
  • create();
  • sockaddr_in addr;
  • int len = sizeof(sockaddr_in);
  • s = ::accept(listen_sock.s, (sockaddr*)&addr, &len);
  • if(s == INVALID_SOCKET) throw socket_error();
  • sock_address.set_port(addr.sin_port);
  • sock_address.set_name(inet_ntoa(addr.sin_addr));
  • }
  • void connect(const address & addr)
  • {
  • create();
  • sockaddr_in sain;
  • int len = sizeof(sockaddr_in);
  • sain.sin_family = AF_INET;
  • sain.sin_port = ::htons(addr.port());
  • hostent* host = ::gethostbyname(addr.name().c_str());
  • if(host == NULL) throw socket_error();
  • memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4);
  • if(::connect(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error();
  • sock_address = addr;
  • }
  • const address & get_address()
  • {
  • return sock_address;
  • }
  • class sock_set : boost::noncopyable
  • {
  • // initialisation d'une instance nulle
  • sock_set(const bool & is_null)
  • : bnull(is_null)
  • {
  • }
  • protected:
  • fd_set fds;
  • bool bnull;
  • public:
  • // renvoit un sock_set nul
  • static sock_set & null()
  • {
  • static sock_set ret(true);
  • return ret;
  • }
  • sock_set()
  • : bnull(false)
  • {
  • FD_ZERO(&fds);
  • }
  • inline void add(const base_sock & _s)
  • {
  • if(bnull) throw socket_error();
  • if(!_s.good()) throw socket_error();
  • FD_SET(_s.s, &fds);
  • }
  • inline void del(const base_sock & _s)
  • {
  • if(bnull) throw socket_error();
  • if(!_s.good()) throw socket_error();
  • FD_CLR(_s.s, &fds);
  • }
  • inline bool is_set(const base_sock & _s) const
  • {
  • if(bnull) throw socket_error();
  • if(!_s.good()) throw socket_error();
  • return FD_ISSET(_s.s, &fds) != 0;
  • }
  • static int select(sock_set & read_set, sock_set & write_set, sock_set & error_set, const timeval & timeout)
  • {
  • if(read_set.bnull && write_set.bnull && error_set.bnull) throw socket_error();
  • fd_set * r = read_set.bnull ? NULL : &(read_set.fds);
  • fd_set * w = write_set.bnull ? NULL : &(write_set.fds);
  • fd_set * e = error_set.bnull ? NULL : &(error_set.fds);
  • int ret = ::select(0, r, w, e, &timeout);
  • if(ret == SOCKET_ERROR) throw socket_error();
  • return ret;
  • }
  • };
  • };
  • #endif
#ifndef sock_h
#define sock_h

#include <boost/utility.hpp>
#include <string>

#ifdef WIN32
	#include <winsock.h>
	#define wsastart(); {WSADATA WsaData; WSAStartup(MAKEWORD(1,1), &WsaData);}
	#define wsaclean(); WSACleanup();
	#pragma comment(lib, "ws2_32.lib")
#else
	#define wsastart();
	#define wsaclean();
	#include <unistd.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <netdb.h>
	#include <sys/types.h>
	#define SOCKET_ERROR (-1) /* défini dans winsock.h */
	typedef int SOCKET;
	#define INVALID_SOCKET (SOCKET)(~0) /* défini dans winsock.h */
	#define closesocket close
#endif

struct socket_error : std::exception {};

struct base_sock : boost::noncopyable
{
	struct address
	{
	protected:
		std::string _name;
		unsigned short _port;

	public:
		address(const address & other)
			: _name(other._name), _port(other._port)
		{
		}

		address(const std::string & __name, const unsigned short & __port)
			: _name(__name), _port(__port)
		{
		}

		address(const std::string & __name)
			: _name(__name), _port(0)
		{
		}

		address(const unsigned short & __port)
			: _port(__port)
		{
		}

		address()
			: _port(0)
		{
		}

		address & operator = (const address & other)
		{
			_name = other._name;
			_port = other._port;
			return *this;
		}
		
		void clear()
		{
			_name = "";
			_port = 0;
		}

		bool is_empty() const
		{
			return _name == "" && _port == 0;
		}

		const unsigned short & port() const
		{
			return _port;
		}

		const std::string & name() const
		{
			return _name;
		}

		unsigned short & set_port(const unsigned short & port)
		{
			return _port = port;
		}

		std::string & set_name(const std::string & name)
		{
			return _name = name;
		}

		bool operator == (const address & other) const
		{
			return (_name == other._name) && (_port == other._port);
		}
	};
	
protected:
	address sock_address;
	SOCKET s;

private:
	void create()
	{
		close();
		s = socket(AF_INET, SOCK_STREAM, 0);
		if(s == INVALID_SOCKET) throw socket_error();
	}

public:

	base_sock()
		: s(INVALID_SOCKET)
	{
		wsastart();
	}
	
	virtual ~base_sock()
	{
		close();
		wsaclean();
	}
	
	bool good() const
	{
		return s != INVALID_SOCKET;
	}

	virtual void write(const std::string & data) const
	{
		if(!good()) throw socket_error();
		if(::send(s, data.data(), (int)data.size(), 0) == SOCKET_ERROR) throw socket_error();
	}

	virtual std::string read() const
	{
		if(!good()) throw socket_error();

		// on crée un buf avec la taille max de data qu'il peut recevoir
		unsigned long cbdata;
		int cbopt = sizeof(cbdata);
		if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&(cbdata), &cbopt) == SOCKET_ERROR) throw socket_error();
		char* data = new char[cbdata];

		// on recoit
		int len;
		if((len = recv(s, data, cbdata, 0)) == SOCKET_ERROR)
		{
			delete[] data;
			throw socket_error();
		} else {
			std::string ret(data, len);
			delete[] data;
			return ret;
		}
	}
	
	void close()
	{
		if(!good()) return;
		::closesocket(s);
		sock_address.clear();
		s = INVALID_SOCKET;
	}

	void listen(const address & addr)
	{
		create();

	    sockaddr_in sain;
	    sain.sin_family = AF_INET;
		sain.sin_port = ::htons(addr.port());

		if(addr.name() == "")
	    sain.sin_addr.s_addr = INADDR_ANY;
		else
		{
			hostent* host = ::gethostbyname(addr.name().c_str());
			if(host == NULL) throw socket_error();
			memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4);
		}

		if(::bind(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error();
		sock_address = addr;

		if(::listen(s, SOMAXCONN) == SOCKET_ERROR) throw socket_error();
	}

	void accept(const base_sock & listen_sock)
	{
		if(!listen_sock.good()) throw socket_error();
		
		create();

		sockaddr_in addr;
		int len = sizeof(sockaddr_in);
		s = ::accept(listen_sock.s, (sockaddr*)&addr, &len);
		if(s == INVALID_SOCKET) throw socket_error();

		sock_address.set_port(addr.sin_port);
		sock_address.set_name(inet_ntoa(addr.sin_addr));
	}
	
	void connect(const address & addr)
	{
		create();

		sockaddr_in sain;
		int len = sizeof(sockaddr_in);
	    sain.sin_family = AF_INET;
		sain.sin_port = ::htons(addr.port());

		hostent* host = ::gethostbyname(addr.name().c_str());
		if(host == NULL) throw socket_error();
		memcpy((void*)&sain.sin_addr, (void*)host->h_addr, 4);
		
		if(::connect(s, (struct sockaddr *) &sain, sizeof(struct sockaddr_in)) == SOCKET_ERROR) throw socket_error();
		sock_address = addr;
	}

	const address & get_address()
	{
		return sock_address;
	}

	class sock_set : boost::noncopyable
	{
		// initialisation d'une instance nulle
		sock_set(const bool & is_null)
			: bnull(is_null)
		{
		}

	protected:
		fd_set fds;
		bool bnull;

	public:
		
		// renvoit un sock_set nul
		static sock_set & null()
		{
			static sock_set ret(true);
			return ret;
		}

		sock_set()
			: bnull(false)
		{
			FD_ZERO(&fds);
		}

		inline void add(const base_sock & _s)
		{
			if(bnull) throw socket_error();
			if(!_s.good()) throw socket_error();
			FD_SET(_s.s, &fds);
		}

		inline void del(const base_sock & _s)
		{
			if(bnull) throw socket_error();
			if(!_s.good()) throw socket_error();
			FD_CLR(_s.s, &fds);
		}

		inline bool is_set(const base_sock & _s) const
		{
			if(bnull) throw socket_error();
			if(!_s.good()) throw socket_error();
			return FD_ISSET(_s.s, &fds) != 0;
		}

		static int select(sock_set & read_set, sock_set & write_set, sock_set & error_set, const timeval & timeout)
		{
			if(read_set.bnull && write_set.bnull && error_set.bnull) throw socket_error();

			fd_set * r = read_set.bnull ? NULL : &(read_set.fds);
			fd_set * w = write_set.bnull ? NULL : &(write_set.fds);
			fd_set * e = error_set.bnull ? NULL : &(error_set.fds);
			
			int ret = ::select(0, r, w, e, &timeout);
			if(ret == SOCKET_ERROR) throw socket_error();
			return ret;
		}

	};
};

#endif

 Conclusion

je l'ai testé sous windows (vc++7) ca a l'air de bien fonctionner.
normalement il devrait compiler/fonctionner sous linux, si qq1 pouvait tester (je n'en ai plus sous la main)

j'essaierais rapidement de dériver base_sock pour supporter la cryptographie, et pour respecter l'interface des i/o streams standards du c++, ainsi que d'améliorer la gestion d'erreur.

merci de me signaler tout bug, suggestion ou demande d'explication :)

14/01 : maj

correction de 2-3 petites fautes
petites modifications pour faciliter la dérivation
suppression de la lourdeur d'utilisation du template select par la mise en place d'un sock_set null

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 Sources du même auteur

Source avec Zip STATCKTRACE : GARDER LA TRACE DE LA PILE D'APPEL
Source avec Zip HOOKING SOUS NT AVEC CREATEREMOTETHREAD (VC++7, COMPILABLE A...
Source avec Zip CLASSE POUR TRAVAILLER AVEC DES GRANDS NOMBRES ENTIERS
Source avec Zip BOT IRC
Source avec Zip EXEMPLE DE SERVEUR (MULTI-CLIENTS) TCP (WIN32 / VC++) (11 SE...

 Sources de la même categorie

Source avec Zip [C++][W IN]LIBRAIRIE HTTP par nipepsinicolas
Source avec Zip ASPIRATEUR RÉCURSIF MACOS par tontonCD
Source avec Zip INTÉGRATION SIMPLE D'UN CLIENT IRC À UNE APPLICATION par PJulot
LIBRAIRIE POUR SOCKETS C++ par genetApt151
Source avec Zip COVERT ART IMAGE par nanonavich

Commentaires et avis

Commentaire de xterminhate le 18/01/2004 01:54:12

Blackgoddess,

   J'attendais avec impatience le résultat de ton travail.

   Alors voici quelques (petites) remarques pour commencer. Si l'utilisateur manipules plusieurs instances de base_sock, il execute plusieurs fois WSAStart et WSAClean...aucune idée de l'impact mais un vieux flag static pourrait permettre de l'éviter.

   D'autre part, si l'utilisateur souhaite exploiter base_sock pour transmettre de grandes quantitiés d'informations, alors il est nécessaire d'implementer un mécanisme de fragmentation dans write() (contenant l'appel de send()). En effet, la taille du buffer à envoyer ne peut pas exceder SO_MAX_MSG_SIZE. Cela doit etre transparent pour l'utilisateur.

   A suivre...

Cordialement,
   Xter.
  

Commentaire de BlackGoddess le 18/01/2004 14:29:04

pour les appels a WSAStartup et WSACleanup :
lorsqu'un processus appelle une fois WSAStartup, les dll réseau sont chargées. puis, lorsqu'on le rappelle une 2eme fois, un compteur interne estr sulement incrémenté. pour les appels a WSACleanup, le compteur interne est décrémenté et lorsqu'il atteint 0 les dll sont déchargées.
par contre tu as peut-etre raison dans le sens ou un appel de WSAStartup va négocier la version entre celle des dll reseau et celle que ton prog demande, et ce n'est pas très optimisé de refaire cette négociation à chaque instanciation.

ensuite, pour les grandes quantités d'informations, il faut bien comprendre que ceci n'est pour l'instant qu'une encapsulation 'basique' de l'api c socket. Je suis en train de développer d'autres classes de dérivant de celle-ci pour supporter un transfert crypté, la fragmentation de paquets comme tu en parle, et faire respecter l'interface istream / ostream standard c++ pour la lecture/ecriture d'un socket.

en tout cas merci de ces remarques :)

Commentaire de xterminhate le 18/01/2004 14:55:33

Je pense que tu devrais intégrer une fragmentation basique dans la fonction membre write. Uniquement basée sur la taille max du buffer d'émission. Et je m'explique.

On constate trop souvent "des fuites" dans nos abstractions et cela est dommagable. En effet, l'utilisateur de l'abstraction base_sock n'est pas censée connaitre les limitations des fonctions de la librairie socket que tu encapsules (en particulier la fonction send).

D'ailleurs, la couche logicielle plus avancée (que tu programmes) et qui exploite base_sock n'est pas non plus censée connaitre ses limitations. Imagine que tu developpes cette couche logicielle avancée à partir d'une absctration base_sock que tu n'aurais pas programmé....

Donc, pour être une parfaite abstraction, base_sock devrait implementer ce mécanisme de fragmentation.

Bon courage pour la suite !

Cordialement,
   Xter.

Commentaire de xterminhate le 18/01/2004 15:53:52

Dans read, tu testes la valeur de retour de recv() selon deux cas (&gt;=0 ou SOCKET_ERROR).

Il faut distinguer les cas &gt;0 et =0. En effet, =0 signifie que la connexion a été fermée.

Cordialement,
   Xter.

Commentaire de BlackGoddess le 18/01/2004 18:10:26

tu as en effet parfaitement raison, je vais corriger ca :)
merci bcp :)

Commentaire de BlackGoddess le 19/01/2004 01:00:11

je viens de lire dans les msdn l'aide pour le paramètre SO_MAX_MSG _SIZE :
Maximum size of a message for message-oriented socket types (for example, SOCK_DGRAM). Has no meaning for stream oriented sockets.

de plus, à l'appel de getsockopt il me dit que la taille maximum est 2^32, valeur stockée dans un unsigned int qui ne peut pas contenir de nombre plus grand.

ansi, apparement ce serait déjà en interne que le message serait fractionné.

Commentaire de xterminhate le 19/01/2004 08:29:51

Certes.

Penses tout de même à comparer la valeur de retour de send() avec la taille du message à envoyer. Si la valeur de retour est inférieure à la taille du message, alors il faut recommencer l'émission avec ce qui reste à envoyer du message. Ainsi de suite, jusqu'à ce que le message soit complement envoyé...

Merci,
   Xter.

Commentaire de BlackGoddess le 19/01/2004 09:31:41

mmh oui en effet :)

Commentaire de xterminhate le 19/01/2004 12:13:57

Au fait, les fonctions de chiffrement/intégrité que tu souhaites ajouter à ton projet m'interessent fortement. Sur quoi vas tu te baser pour les réaliser ?

Cordialement,
   Xter.

Commentaire de BlackGoddess le 19/01/2004 14:20:08

bin ... j'ai codé un module de crypto orientée message avec le cryptage rsa et triple des (plutot double des :p).

je fais pas trop de commentaire la dessus pour l'instant, j'en ferais qd je poserais la source. (triple des =&gt; source sur le net, rsa =&gt; grace a la classe BigInt sur ce site :) )

Commentaire de BlackGoddess le 19/01/2004 14:21:15

mais bon vu ton interet je ferais donc le support cryptage avant le support i/ostream

 Ajouter un commentaire




Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Septembre 2010
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
27282930   

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,451 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales