Librairie pour sockets c++

Soyez le premier à donner votre avis sur cette source.

Snippet vu 15 257 fois - Téléchargée 26 fois

Contenu du snippet

Voici une source permettant de créer des clients et des serveurs basées sur les sockets pour système UNIX.

Pour cela il suffit d'implémenter la méthode virtuelle interaction.
La partie connexion est gérée automatiquement. Chaque connexion d'un client est gérée par un thead.

note : ne fonctionne pas sous windows.

La source contient un exemple d'implémentation d'un client et d'un serveur.

Le serveur cle/valeur implémente les operations suivantes:
- GET CLE
- PUT CLE VALEUR
- DEL CLE
- QUIT

Les applications doivent être lancées à partir du terminal :
serveur : ./server-word-word 23000
client : ./client-word-word localhost 23000

telecharger source : http://www.megaupload.com/?d=VKWF64L6

Source / Exemple :


#ifndef _SOCKET_HH_
#define _SOCKET_HH_

#include "libsocketException.h"

extern "C"
{
	#include <stdlib.h>
	#include <stdio.h>
};

#include <iostream>
#include <sstream>

namespace libsocket
{
 /*____________________________________________________________________________________________*/
  /** 

  • une socket.
    • /
class Socket { protected: int _fd;// descripteur de la socket virtual void socket() = 0; virtual void bind(int port) = 0; public: Socket(); virtual void close()=0; void setFd(int fd); int getFd(); }; } #endif #include "libsocketTcp.h" extern "C" { #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <errno.h> #include <netinet/in.h> #include <string.h> }; #include <iostream> #include <sstream> #include <pthread.h> // librairie thread namespace libsocket { /*************************************************************************/ /** Server **/ /*************************************************************************/ /*_____________________________________________________________________________*/ /**
    • /
void ServerTcp::run(int port) { socket(); bind(port); listen(10); loop(); } /*_____________________________________________________________________________*/ /**
  • boucle d'intéraction
    • /
void ServerTcp::loop() { /*while (true) { SocketTcp client; accept(client); std::cout<<"apres:"<<client.getFd()<<std::endl; if(fork() == 0) { printf("connexion \n"); interaction(client); client.close(); printf("deconnexion \n"); exit(0); } }*/ while (true) { SocketTcp* client = new SocketTcp(); accept(*client); pthread_t num_thread[1]; void * tab[2] = {this,client}; if (pthread_create(&num_thread[0], NULL, (void *(*)(void *))&ServerTcp::startThread,tab) == -1) perror ("problème creation du thread\n"); } } /*_____________________________________________________________________________*/ /**
  • on est obligé de passer par une métode static pour utiliser un thread
    • /
void* ServerTcp::startThread ( void* Parameters[] ) { ServerTcp* server = reinterpret_cast<ServerTcp*>(Parameters[0]);//recupere l'objet Server SocketTcp* client = reinterpret_cast<SocketTcp*>(Parameters[1]);//recupere la socket client server->runThread(client); } /*_____________________________________________________________________________*/ /**
  • methode appelé par le thread
    • /
void ServerTcp::runThread(SocketTcp* client) { printf("connexion\n"); interaction(*client);//appel de la methode virtuelle interaction client->close();//fermeture de la socket delete client;//liberation du pointeur client déclaré dans Server::loop printf("deconnexion \n"); pthread_exit(NULL);//detruit le thread } /*************************************************************************/ /** Client **/ /*************************************************************************/ /*_____________________________________________________________________________*/ /**
  • utilise les méthodes de Socket pour établir une connexion
  • à une serveur lancer l'interaction
    • /
void ClientTcp::run(const char* host, int port) { socket(); connect(host,port); interaction(); close(); } /*_____________________________________________________________________________*/ /**
  • cette variante appelle la précédente
    • /
void ClientTcp::run(string& host, int port) { run(host.c_str(),port); } }; #include "socketTcp.h" #include <iostream> using namespace std; namespace libsocket { /*_____________________________________________________________________________*/ /**
  • invoque l'appel système socket
    • /
void SocketTcp::socket() { _fd = ::socket(PF_INET, SOCK_STREAM, 0); // appel la fonction socket de c. if (_fd < 0) throw ErrnoExcept("socket"); } /*_____________________________________________________________________________*/ void SocketTcp::connect(const char* host, int port) { ostringstream os; os<<port; socket(); struct addrinfo *serv_addr = 0; int k = getaddrinfo(host,os.str().c_str(),0,&serv_addr);//obtebir une structure d adresse if(k != 0) throw ErrnoExcept("getaddrinfo"); while(k=::connect(_fd,serv_addr->ai_addr,serv_addr->ai_addrlen)) { if(k<0) if (errno==EAGAIN || errno==EINTR) continue; else throw ErrnoExcept("serveur indisponible"); } freeaddrinfo(serv_addr); } /*_____________________________________________________________________________*/ /**
  • ferme la socket
    • /
void SocketTcp::close() { while (_fd >= 0) { int r = ::close(_fd); if (r < 0) { if (errno == EINTR) continue; else throw ErrnoExcept("close"); } break; } _fd = -1; } /*_____________________________________________________________________________*/ /**
  • lie la socket au port sur toutes les interfaces.
    • /
void SocketTcp::bind(int port) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (::bind(_fd, (struct sockaddr *) &addr, sizeof(addr))==-1) throw ErrnoExcept("bind"); } /*_____________________________________________________________________________*/ /**
  • \c listen pour indiquer qu'on va écouter sur cette socket.
    • /
void SocketTcp::listen(int backlog) { if (::listen(_fd, backlog)==-1) throw ErrnoExcept("listen"); } /*_____________________________________________________________________________*/ /**
  • Pour attendre et accepter une demande de connexion d'un client. C
    • /
void SocketTcp::accept(Socket& client) { while (true) { int cli = ::accept(_fd, NULL, NULL); if (cli < 0) { //cas exceptionnels de fonctionnement normal if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; else throw ErrnoExcept("accept"); } client.setFd(cli); break; } } /*_____________________________________________________________________________*/ /**
  • écrit sur la socket exactement n octets pointés par buf.
    • /
void SocketTcp::write(const char* buf, int n) { while (n > 0) { int k = ::write(_fd, buf, n); if (k < 0) { if (errno == EAGAIN || errno == EINTR) continue; else throw ErrnoExcept("write"); } buf += k; n -= k; } } /*_____________________________________________________________________________*/ /**
  • ecrit sur la socket en calculant elle-même la
  • longueur de la chaîne de caractères.
    • /
void SocketTcp::write(const char* buf) { write(buf, strlen(buf)+1);//+1 pour compter le '\0' } /*_____________________________________________________________________________*/ /** /**
  • Pour attendre et accepter une demande de connexion d'un client.
    • /
void SocketTcp::write(const string& buf) { write(buf.c_str(), buf.length()+1);//+1 pour compter le '\0' } /*_____________________________________________________________________________*/ void SocketTcp::read(char *buf,int n) { while (n > 0) { int k = ::read(_fd, buf, n); if (k < 0) if (errno == EAGAIN || errno == EINTR) continue; else throw ErrnoExcept("read"); buf += k; n -= k; } } /*_____________________________________________________________________________*/ /**
  • lit sur la socket exactement n octets et les retourne sous
la forme d'un string. (ne pas compter le caractère de fin de chaine '\0')
    • /
string SocketTcp::read(int n) { char mes[n+1]; //a corriger !! read(mes,n); int taille = strlen(mes); if(mes[taille] != '\0') mes[taille+1] = '\0'; return (string) mes; } /*_____________________________________________________________________________*/ /**
  • lire un entier
    • /
int32_t SocketTcp::readInt32() { int32_t v; read((char *) &v,sizeof(v)); return ntohl(v); } /*_____________________________________________________________________________*/ /**
  • ecrire un entier
    • /
void SocketTcp::writeInt32(int i) { int32_t v = htonl(i); write (( const char *) &v,sizeof(v)); } /*_____________________________________________________________________________*/ /**
  • lit une ligne de texte et retourne un string (sans le \n)
    • /
string SocketTcp::readLine() { char c; ostringstream os; while(true) { int k = ::read(_fd,&c,1); if (k <= 0)//si k==0 on leve une exception pour eviter une boucle infinie if (errno == EAGAIN || errno == EINTR) continue; else throw ErrnoExcept("readline"); if(c == EOF || c == '\0' || c == '\n') break; else os << c; } return os.str(); } /*_____________________________________________________________________________*/ /**
  • lit un mot délimité par un espaces.
    • /
string SocketTcp::readWord() { char c; ostringstream res ; int k; //supprime les espaces while(true) { k = ::read(_fd,&c,1); if (k <= 0) //si k==0 on leve une exception pour eviter une boucle infinie if (errno == EAGAIN || errno == EINTR) continue; else throw ErrnoExcept("readword"); if(c == EOF || c == '\n' || c == '\0') return ""; if(!isspace(c)) { res<<c; break; } } //lecture d'un mot while(true) { k= ::read(_fd,&c,1); if (k <= 0) if (errno == EAGAIN || errno == EINTR) continue; else throw ErrnoExcept("readword"); if(c == EOF || c == '\0' || c == '\n' || isspace(c)) break; else res << c; } return res.str(); } } #include "socketUdp.h" #include <iostream> using namespace std; namespace libsocket { /*_____________________________________________________________________________*/ /**
  • invoque l'appel système socket
    • /
void SocketUdp::socket() { _fd = ::socket(AF_INET, SOCK_DGRAM, 0); // appel la fonction socket de c. if (_fd < 0) throw ErrnoExcept("socket"); } /*_____________________________________________________________________________*/ /**
  • ferme la socket
    • /
void SocketUdp::close() { while (_fd >= 0) { int r = ::close(_fd); if (r < 0) { if (errno == EINTR) continue; else throw ErrnoExcept("close"); } break; } _fd = -1; } /*_____________________________________________________________________________*/ /**
  • lie la socket au port sur toutes les interfaces.
    • /
void SocketUdp::bind(int port) { this->addr.sin_family = AF_INET; this->addr.sin_port = htons(port); this->addr.sin_addr.s_addr = htonl(INADDR_ANY); if (::bind(this->_fd, (struct sockaddr *) &this->addr, sizeof(this->addr))!=0) throw ErrnoExcept("bind"); } /*_____________________________________________________________________________*/ /**
  • envoyer un message.
    • /
void SocketUdp::sendto(const char* mes) { ::sendto(this->getFd(),mes,strlen(mes)+1,0,(struct sockaddr*)&this->addr,sizeof(addr)); } /*_____________________________________________________________________________*/ /**
  • envoyer un message.
    • /
void SocketUdp::sendto(string mes) { sendto(mes.c_str()); } /*_____________________________________________________________________________*/ /**
  • recevoir un message.
    • /
string SocketUdp::recvfrom(int n) { char mes[n+1]; int flag = ::recvfrom(this->getFd(),mes,n,0,(struct sockaddr*)&this->addr,&this->lg_app); int taille = strlen(mes); if(mes[taille] != '\0') mes[taille+1] = '\0'; return (string) mes; } }

A voir également

Ajouter un commentaire

Commentaires

rpoline
Messages postés
1
Date d'inscription
jeudi 23 septembre 2004
Statut
Membre
Dernière intervention
1 juin 2010

Le code est effectivement assez jolie mais pas en adéquation avec une utilisation intensive.

Typiquement dans ce genre d'application il vaut mieux écrire une fonction qui ré-assemble les données en provenance de la socket (si fragmentation) puis décoder la trame complète en provenance du Réseau avec une méthode dédiée.

J'ai pour ma part développer il y à quelques années développer une Solution Client/Serveur capable de gérer 600 connexions simultanées il nous a fallu 6 mois pour mettre au point le système.....

Richard
YahyaHajji
Messages postés
5
Date d'inscription
lundi 29 décembre 2008
Statut
Membre
Dernière intervention
19 juillet 2010

sboli
Messages postés
10
Date d'inscription
vendredi 14 août 2009
Statut
Membre
Dernière intervention
31 mai 2010

Ca peut paraitre bizarre comme remarque, mais ton niveau d'abstraction n'est pas assez élevé.
Dans une classe comme celle-ci, close(), bind(), listen(), accept() ne devrait pas être accessibles. A la limite j'aurais fait ça avec deux fonctions: read(unsigned), write(const Buffer&).

Au lieu de te prendre la tête avec les int8/16/32 tu peut envoyer n'importe que quoi (qui a son op<< sur ostream) en sérialisant via std::ostringstream.
Ks4ssPeuk
Messages postés
1
Date d'inscription
mardi 27 octobre 2009
Statut
Membre
Dernière intervention
31 mai 2010

Je ne sais pas si tu es de l'IUT d'Orleans, mais il me semble que ce code source est calqué à 100% sur le code de Mr Duchier. Si c'est le cas c'est pas beau de faire ça ...
LeFauve42
Messages postés
239
Date d'inscription
vendredi 20 octobre 2006
Statut
Membre
Dernière intervention
20 avril 2009

Bonjour,

Ca a l'air pas mal, mais je ne vois aucun mecanisme de bufferisation.
Envoyer des bytes ou des shorts un par un peu vraiment ralentir le trafic.
Il vaut mieux preparer un buffer de quelques Ko et faire un flush quand on veut tout envoyer (j'ai reussi a diviser mes temps de transmission par 10 grace a ce genre d'ameliorations).

C'est surtout efficace si l'un des deux hosts est tres vieux (dans mon cas, c'etait une communication avec un robot tournant sous Win98SE) mais ca doit quand meme aider avec des machines plus recentes.

Dans le meme genre, tes fonctions put_uintxx et get_uintxx sont tres inefficaces (faire 4 appels systemes pour ecrire 32 bits c'est (au moins) 3 de trop. Et je ne parle meme pas de tes fonctions pour lire une ligne ou un mot, qui lisent la socket caractere par caractere...

Pour faire simple, ton code est tres beau, mais du coup tres ineficace.
L'interet d'utiliser une lib avec des classes de haut niveau est aussi de pouvoir planquer dedans des trucs moins beaux mais efficaces (surtout pour des trucs de bas niveau comme les sockets).
Les gens qui utilisent la lib (si elle marche bien) ne regardent jamais dedans, alors autant faire un truc le plus efficace possible (tant que l'interface est jolie et pratique a utiliser).

Eric

NB: Si tu fais des tests de performance pour verifier ce que je te dis, utilise bien 2 machines, car sur le meme pc, les optimisations du loopback device (127.0.0.1) vont certainement masquer les defauts.

NB2: Si tu veux t'amuser a piloter un robot pour tester ta lib de sockets, on cherche des volontaires :o)

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.