commit
4dd7b13e53
25 changed files with 2293 additions and 0 deletions
@ -0,0 +1,70 @@ |
|||
***** PARTIE C++ ***** |
|||
|
|||
Etape 1 : OK |
|||
|
|||
Etape 2 : OK |
|||
|
|||
Etape 3 : OK |
|||
|
|||
Etape 4 : |
|||
|
|||
--- Comment appelle-t'on ce type de méthode et comment faut-il les déclarer ? |
|||
--> Ce type de méthode est une méthode abstraite, il faut la déclarer en |
|||
--> rajoutant const = 0 dans le header |
|||
|
|||
Etape 5 : |
|||
|
|||
--- Quelle est la propriété caractéristique de l'orienté objet qui permet de faire cela ? |
|||
--> C'est le polymorphisme |
|||
|
|||
--- Qu'est-il spécifiquement nécessaire de faire dans le cas du C++ ? |
|||
--> il faut déclarer les méthodes "virtual" |
|||
|
|||
--- Quel est le type des éléments du tableau : est-ce que ce tableau contient |
|||
--- les objets ou des pointeurs vers ces objets ? |
|||
---> Ce tableau contient des pointeurs vers ces objets |
|||
|
|||
Etape 6 : |
|||
|
|||
--- Que faut-il faire pour que l'objet Film ait plein contrôle sur ses données |
|||
--- et que son tableau de durées des chapitres ne puisse pas être modifié (ou pire, détruit) à son insu ? |
|||
--> Il faut recopier le tableau passé en paramètre |
|||
|
|||
--- Quelle est la solution très simple que propose C/C++ pour éviter ce problème ? |
|||
--> Il suffit de déclarer la méthode const * |
|||
|
|||
Etape 7 : |
|||
|
|||
--- Parmi les classes précédemment écrites quelles sont celles qu'il faut modifier |
|||
--- afin qu'il n'y ait pas de fuite mémoire quand on détruit leurs instances ? |
|||
--> Il faut modifier la classe Film car elle possède un attribut dont le type |
|||
--> n'est pas un type de base |
|||
|
|||
--- Pourquoi et que faudrait-il faire ? |
|||
--> La copie d'objets peut poser problème car ce serait une copie superficielle, |
|||
--> Les attributs qui sont des pointeurs pointeraient vers le meme objet. |
|||
--> Il faut alors redéfinir l'opérateur = et le constructeur de copie |
|||
|
|||
Etape 8 : |
|||
|
|||
--- On rappelle aussi que la liste d'objets doit en fait être une liste de pointeurs d'objets. Pourquoi ? |
|||
--> Si on veut qu'un objet puisse apartenir à plusieurs groupes, il faut que chaque |
|||
--> groupe contienne des pointeurs vers les objets, pour ne pas dupliquer les objets |
|||
--> En java, il n'y a pas de pointeurs explicite, tout est fait implicitement |
|||
|
|||
Etape 9 : OK |
|||
|
|||
Etape 10 : |
|||
|
|||
--- Comment peut-on l'interdire, afin que seule la classe servant à manipuler les objets puisse en créer de nouveaux ? |
|||
--> On peut rendre le constructeur private et mettre en amie la classe Gestion |
|||
|
|||
Etape 11 : OK |
|||
|
|||
Etape 12 : non traitée |
|||
|
|||
Etape 13 : non traitée |
|||
|
|||
***** PARTIE JAVASWING ***** |
|||
|
|||
Utilisation des AbstractAction |
Binary file not shown.
@ -0,0 +1,36 @@ |
|||
#include "Base.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
using namespace std; |
|||
|
|||
Base::Base() { |
|||
|
|||
} |
|||
|
|||
Base::Base(string name, string pathname) : name(name), pathname(pathname) { |
|||
} |
|||
|
|||
Base::~Base() { |
|||
|
|||
} |
|||
|
|||
void Base::setName(string _name) { |
|||
name = _name; |
|||
} |
|||
|
|||
void Base::setPathName(string _pathname) { |
|||
pathname = _pathname; |
|||
} |
|||
|
|||
string Base::getName() const { |
|||
return name; |
|||
} |
|||
|
|||
string Base::getPathName() const { |
|||
return pathname; |
|||
} |
|||
|
|||
void Base::affichage(ostream& flux) const { |
|||
flux << "Le fichier : " << name << " a pour chemin : " << pathname << endl; |
|||
} |
@ -0,0 +1,36 @@ |
|||
#ifndef BASE_H |
|||
#define BASE_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
|
|||
/**
|
|||
* @brief Classe de base des fichiers multimedias |
|||
* |
|||
* les méthodes @e setX et @e getX sont les getteurs et les setteurs |
|||
* des attributs @e name et @e pathname |
|||
* |
|||
* la méthode @e affichage permet d'afficher sur le flux donné |
|||
* les attributs |
|||
* |
|||
* la méthode play est abstraite et sera implémentée par les sous-classes |
|||
* en effet elle dépend du type de fichier |
|||
*/ |
|||
|
|||
class Base { |
|||
protected: |
|||
std::string name{}; |
|||
std::string pathname{}; |
|||
public: |
|||
Base(); |
|||
Base(std::string, std::string); |
|||
virtual ~Base(); |
|||
void setName(std::string); |
|||
void setPathName(std::string); |
|||
std::string getName() const; |
|||
std::string getPathName() const; |
|||
virtual void affichage(std::ostream&) const; |
|||
virtual void play() const = 0; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,44 @@ |
|||
#include "Video.h" |
|||
#include "Film.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
using namespace std; |
|||
|
|||
Film::Film(string name, string pathname, unsigned int duree) : Video(name, pathname, duree) { |
|||
} |
|||
|
|||
Film::~Film() { |
|||
cout << "film détruit" << endl; |
|||
delete[] chapitres; |
|||
} |
|||
|
|||
Film::Film(Film const& filmACopier) : Video(filmACopier.name, filmACopier.pathname, filmACopier.duree), nbChapitres(filmACopier.nbChapitres) { |
|||
chapitres = new unsigned int[filmACopier.nbChapitres]; |
|||
for (unsigned int i(0); i<filmACopier.nbChapitres; i++) { |
|||
chapitres[i] = filmACopier.chapitres[i]; |
|||
} |
|||
} |
|||
|
|||
void Film::setChapitres(unsigned int const * _chapitres, unsigned int size) { |
|||
delete[] chapitres; |
|||
chapitres = new unsigned int[size]; |
|||
nbChapitres = size; |
|||
for (unsigned int i(0); i<size; i++) { |
|||
chapitres[i] = _chapitres[i]; |
|||
} |
|||
} |
|||
|
|||
unsigned int const * Film::getChapitres() const { |
|||
return chapitres; |
|||
} |
|||
|
|||
unsigned int Film::getNbChapitres() const { |
|||
return nbChapitres; |
|||
} |
|||
|
|||
void Film::afficheChapitres(std::ostream& flux) const { |
|||
for (unsigned int i(0); i < nbChapitres; i++) { |
|||
flux << "le chapitre " << i +1 << " a pour durée : " << chapitres[i] << endl; |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
#ifndef FILM_H |
|||
#define FILM_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include "Video.h" |
|||
|
|||
/**
|
|||
* @brief Classe des Films, héritant de la classe @e Video |
|||
* |
|||
* Par rapport aux vidéos, les films ont des chapitres |
|||
* on trouve donc pour cela deux attributs supplémentaires |
|||
* |
|||
* les getteurs et les setteurs correspondent à ces nouveaux attributs |
|||
* |
|||
* il a été également nécessaire de réimplémenter l'opérateur @e = |
|||
*/ |
|||
|
|||
class Film : public Video { |
|||
private: |
|||
unsigned int nbChapitres{}; |
|||
unsigned int * chapitres{}; |
|||
public: |
|||
Film(std::string, std::string, unsigned int = 0); |
|||
Film(Film const&); |
|||
void setChapitres(unsigned int const *, unsigned int); |
|||
unsigned int const * getChapitres() const; |
|||
unsigned int getNbChapitres() const; |
|||
void afficheChapitres(std::ostream&) const; |
|||
virtual ~Film(); |
|||
|
|||
Film& operator=(Film const& filmACopier) { |
|||
if (this != &filmACopier) { |
|||
name = filmACopier.name; |
|||
pathname = filmACopier.pathname; |
|||
duree = filmACopier.duree; |
|||
nbChapitres = filmACopier.nbChapitres; |
|||
delete [] chapitres; |
|||
chapitres = new unsigned int[filmACopier.nbChapitres]; |
|||
for (unsigned int i(0); i<filmACopier.nbChapitres; i++) { |
|||
chapitres[i] = filmACopier.chapitres[i]; |
|||
} |
|||
} |
|||
|
|||
return *this; |
|||
} |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,79 @@ |
|||
#include "Gestion.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
#include <map> |
|||
#include "Base.h" |
|||
#include "Groupe.h" |
|||
#include "Photo.h" |
|||
#include "Video.h" |
|||
#include "Film.h" |
|||
|
|||
using namespace std; |
|||
|
|||
PhotoPtr Gestion::creerPhoto(string name, string pathname, unsigned int latitude, unsigned int longitude) { |
|||
PhotoPtr obj(new Photo(name, pathname, latitude, longitude)); |
|||
objets[name] = obj; |
|||
return obj; |
|||
} |
|||
|
|||
VideoPtr Gestion::creerVideo(string name, string pathname, unsigned int duree) { |
|||
VideoPtr obj(new Video(name, pathname, duree)); |
|||
objets[name] = obj; |
|||
return obj; |
|||
} |
|||
|
|||
FilmPtr Gestion::creerFilm(string name, string pathname, unsigned int duree) { |
|||
FilmPtr obj(new Film(name, pathname, duree)); |
|||
objets[name] = obj; |
|||
return obj; |
|||
} |
|||
|
|||
GroupePtr Gestion::creerGroupe(string name) { |
|||
GroupePtr obj(new Groupe(name)); |
|||
groupes[name] = obj; |
|||
return obj; |
|||
} |
|||
|
|||
void Gestion::afficher(string name, ostream& flux) { |
|||
int cpt = 0; |
|||
|
|||
map<string,BasePtr>::iterator it; |
|||
it = objets.find(name); |
|||
if (it != objets.end()) it->second->affichage(flux); |
|||
else cpt += 1; |
|||
|
|||
map<string,GroupePtr>::iterator it2; |
|||
it2 = groupes.find(name); |
|||
if (it2 != groupes.end()) it2->second->affichage(flux); |
|||
else cpt += 1; |
|||
|
|||
if (cpt == 2) flux << "aucun fichier ou groupe pour ce nom : " << name << endl; |
|||
} |
|||
|
|||
void Gestion::jouer(string name) { |
|||
map<string,BasePtr>::iterator it; |
|||
it = objets.find(name); |
|||
if (it != objets.end()) it->second->play(); |
|||
} |
|||
|
|||
void Gestion::supprimer(string name) { |
|||
map<string,GroupePtr>::iterator it2; |
|||
it2 = groupes.find(name); |
|||
if (it2 != groupes.end()) groupes.erase(it2); |
|||
|
|||
map<string,BasePtr>::iterator it1; |
|||
it1 = objets.find(name); |
|||
if (it1 != objets.end()) objets.erase(it1); |
|||
|
|||
for (auto & itg : groupes) { |
|||
itg.second->supprimer(name); |
|||
} |
|||
} |
|||
|
|||
void Gestion::lister(ostream& flux) { |
|||
flux << "Les groupes disponibles et leurs éléments sont les suivants : " << endl; |
|||
for (auto & it : groupes) { |
|||
it.second->affichage(flux); |
|||
} |
|||
} |
@ -0,0 +1,55 @@ |
|||
#ifndef GESTION_H |
|||
#define GESTION_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include <map> |
|||
#include "Base.h" |
|||
#include "Groupe.h" |
|||
#include "Photo.h" |
|||
#include "Video.h" |
|||
#include "Film.h" |
|||
|
|||
/**
|
|||
* @brief Gestion est la classe dont une instance est une base de donnée |
|||
* d'une box média |
|||
* |
|||
* Cette classe comprend deux attributs permettant de manipuler les données |
|||
* Ces deux attributs sont des @e map, un pour les fichier médias, et |
|||
* un pour les groupes (cf la documentation de Groupe) |
|||
* |
|||
* Cette classe possède 4 méthodes de création d'objet : |
|||
* - trois permettant de créer les trois types de média |
|||
* - une permettant de créer un groupe |
|||
* |
|||
* Par création on entend : création de l'objet proprement dit ET ajout de cet |
|||
* objet au @e map correspondant |
|||
* |
|||
* Enfin, quatres méthodes permettent au client d'intéragir avec ces données : |
|||
* - afficher : trouve et affiche un media ou groupe selon sa méthode d'affichage |
|||
* - jouer : trouve et joue un media selon sa méthode de lecture |
|||
* - supprimer : trouve et supprime un media ou un groupe |
|||
* - lister : liste le contenu du @e map @b groupes |
|||
*/ |
|||
|
|||
typedef std::shared_ptr<Photo> PhotoPtr; |
|||
typedef std::shared_ptr<Video> VideoPtr; |
|||
typedef std::shared_ptr<Film> FilmPtr; |
|||
typedef std::shared_ptr<Groupe> GroupePtr; |
|||
|
|||
class Gestion { |
|||
private: |
|||
std::map<std::string, BasePtr> objets; |
|||
std::map<std::string, GroupePtr> groupes; |
|||
public: |
|||
PhotoPtr creerPhoto(std::string, std::string, unsigned int = 0, unsigned int = 0); |
|||
VideoPtr creerVideo(std::string, std::string, unsigned int = 0); |
|||
FilmPtr creerFilm(std::string, std::string, unsigned int = 0); |
|||
GroupePtr creerGroupe(std::string); |
|||
void afficher(std::string,std::ostream&); |
|||
void jouer(std::string); |
|||
void supprimer(std::string); |
|||
void lister(std::ostream&); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,34 @@ |
|||
#include "Groupe.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
using namespace std; |
|||
|
|||
Groupe::Groupe(string name) : name(name) {} |
|||
|
|||
string Groupe::getName() const { |
|||
return name; |
|||
} |
|||
|
|||
void Groupe::affichage(ostream& flux) const { |
|||
flux << "informations du groupe : " << name << endl << "{" << endl; |
|||
for (auto & it : *this) { |
|||
flux << "; - "; |
|||
it->affichage(flux); |
|||
} |
|||
flux << ";}" << endl; |
|||
} |
|||
|
|||
void Groupe::supprimer(string name) { |
|||
list<BasePtr>::iterator it, it_tosuppr; |
|||
it_tosuppr = this->end(); |
|||
it = this->begin(); |
|||
while (it != this->end()) { |
|||
if ((*it)->getName() == name) { |
|||
cout << "erase" << endl; |
|||
it_tosuppr = it; |
|||
} |
|||
++it; |
|||
} |
|||
if (it_tosuppr != this->end()) this->erase(it_tosuppr); |
|||
} |
@ -0,0 +1,33 @@ |
|||
#ifndef GROUPE_H |
|||
#define GROUPE_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include <list> |
|||
#include <memory> |
|||
#include "Base.h" |
|||
|
|||
/**
|
|||
* @brief Classe permettant de regrouper des objets |
|||
* |
|||
* Les objets multimedias peuvent être regroupés par catégories, par groupes |
|||
* Cette classe est une sous-classe de la classe template @e std::list |
|||
* |
|||
* On y trouve une méthode pour récupérer le nom (getteur), une méthode |
|||
* permettant d'afficher chacun des objets que contient un groupe, et une |
|||
* méthode permettant de supprimer un élément |
|||
*/ |
|||
|
|||
typedef std::shared_ptr<Base> BasePtr; |
|||
|
|||
class Groupe : public std::list<BasePtr> { |
|||
private: |
|||
std::string name{}; |
|||
public: |
|||
Groupe(std::string name); |
|||
std::string getName() const; |
|||
void affichage(std::ostream&) const; |
|||
void supprimer(std::string); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,100 @@ |
|||
##########################################
|
|||
#
|
|||
# Exemple de Makefile
|
|||
# Eric Lecolinet - Reda Dehak - Telecom ParisTech 2015
|
|||
# INF224 - TP C++ - http://www.telecom-paristech.fr/~elc/inf224
|
|||
#
|
|||
##########################################
|
|||
|
|||
#
|
|||
# Nom du programme
|
|||
#
|
|||
PROG = myprog |
|||
|
|||
#
|
|||
# Fichiers sources (NE PAS METTRE les .h ni les .o seulement les .cpp)
|
|||
#
|
|||
SOURCES = Base.cpp main.cpp Video.cpp Photo.cpp Film.cpp Groupe.cpp Gestion.cpp tcpserver.cpp ccsocket.cpp |
|||
|
|||
#
|
|||
# Fichiers objets (ne pas modifier sauf si l'extension n'est pas .cpp)
|
|||
#
|
|||
OBJETS = ${SOURCES:%.cpp=%.o} |
|||
|
|||
#
|
|||
# Compilateur C++
|
|||
#
|
|||
CXX = c++ |
|||
|
|||
#
|
|||
# Options du compilateur C++
|
|||
# -g pour debugger, -O optimise, -Wall affiche les erreurs, -I pour les headers
|
|||
# -std=c++11 pour C++11
|
|||
# Exemple: CXXFLAGS= -std=c++11 -Wall -O -I/usr/local/qt/include
|
|||
#
|
|||
CXXFLAGS = -std=c++11 -Wall -g |
|||
|
|||
#
|
|||
# Options de l'editeur de liens
|
|||
#
|
|||
LDFLAGS = |
|||
|
|||
#
|
|||
# Librairies a utiliser
|
|||
# Exemple: LDLIBS = -L/usr/local/qt/lib -lqt
|
|||
#
|
|||
LDLIBS = -lpthread |
|||
|
|||
|
|||
##########################################
|
|||
#
|
|||
# Regles de construction/destruction des .o et de l'executable
|
|||
# depend-${PROG} sera un fichier contenant les dependances
|
|||
#
|
|||
|
|||
all: ${PROG} |
|||
|
|||
run: ${PROG} |
|||
./${PROG} |
|||
|
|||
${PROG}: depend-${PROG} ${OBJETS} |
|||
${CXX} -o $@ ${CXXFLAGS} ${LDFLAGS} ${OBJETS} ${LDLIBS} |
|||
|
|||
clean: |
|||
-@$(RM) *.o depend-${PROG} core 1>/dev/null 2>&1 |
|||
|
|||
clean-all: clean |
|||
-@$(RM) ${PROG} 1>/dev/null 2>&1 |
|||
|
|||
tar: |
|||
tar cvf ${PROG}.tar.gz ${SOURCES} |
|||
|
|||
# Gestion des dependances : creation automatique des dependances en utilisant
|
|||
# l'option -MM de g++ (attention tous les compilateurs n'ont pas cette option)
|
|||
#
|
|||
depend-${PROG}: |
|||
${CXX} ${CXXFLAGS} -MM ${SOURCES} > depend-${PROG} |
|||
|
|||
|
|||
###########################################
|
|||
#
|
|||
# Regles implicites
|
|||
#
|
|||
|
|||
.SUFFIXES: .cpp .cxx .c |
|||
|
|||
.cpp.o: |
|||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
.cxx.o: |
|||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
.c.o: |
|||
$(CC) -c (CFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
|
|||
#############################################
|
|||
#
|
|||
# Inclusion du fichier des dependances
|
|||
#
|
|||
-include depend-${PROG} |
@ -0,0 +1,113 @@ |
|||
########################################## |
|||
# |
|||
# Makefile pour client/serveur TCP |
|||
# Eric Lecolinet - Reda Dehak - Telecom ParisTech 2015 |
|||
# INF224 - TP C++ - http://www.telecom-paristech.fr/~elc/inf224 |
|||
# |
|||
########################################## |
|||
|
|||
# |
|||
# Nom du programme |
|||
# |
|||
CLIENT=client |
|||
SERVER=server |
|||
CLISERV=cliserv |
|||
|
|||
# |
|||
# Fichiers sources (NE PAS METTRE les .h ni les .o mais seulement les .cpp) |
|||
# |
|||
CLIENT_SOURCES=client.cpp ccsocket.cpp |
|||
SERVER_SOURCES=server.cpp tcpserver.cpp ccsocket.cpp |
|||
CLISERV_SOURCES=client.cpp server.cpp tcpserver.cpp ccsocket.cpp Makefile-cliserv |
|||
# |
|||
# Fichiers objets (ne pas modifier, sauf si l'extension n'est pas .cpp) |
|||
# |
|||
CLIENT_OBJETS=${CLIENT_SOURCES:%.cpp=%.o} |
|||
SERVER_OBJETS=${SERVER_SOURCES:%.cpp=%.o} |
|||
|
|||
# |
|||
# Compilateur C++ (suivant les systemes CXX ou CCC) |
|||
# |
|||
CXX= c++ |
|||
|
|||
# |
|||
# Options du compilateur C++ |
|||
# -g pour debugger, -O optimise, -Wall affiche les erreurs, -I pour les headers |
|||
# -std=c++11 pour C++11 |
|||
# Example: CXXFLAGS= -std=c++11 -Wall -O -I/usr/local/qt/include |
|||
# |
|||
CXXFLAGS= -std=c++11 -Wall -g |
|||
|
|||
# |
|||
# Options de l'editeur de liens |
|||
# |
|||
LDFLAGS= |
|||
|
|||
# |
|||
# Librairies a utiliser |
|||
# Example: LDLIBS = -L/usr/local/qt/lib -lqt |
|||
# |
|||
LDLIBS= -lpthread |
|||
|
|||
|
|||
########################################## |
|||
# |
|||
# Regles de construction/destruction des .o et de l'executable |
|||
# depend-${PROG} sera un fichier contenant les dependances |
|||
# |
|||
|
|||
all: ${CLIENT} ${SERVER} |
|||
|
|||
run-${SERVER}: ${SERVER} |
|||
./${SERVER} |
|||
|
|||
run-${CLIENT}: ${CLIENT} |
|||
./${CLIENT} |
|||
|
|||
${CLIENT}: depend-${CLIENT} ${CLIENT_OBJETS} |
|||
${CXX} -o $@ ${CXXFLAGS} ${LDFLAGS} ${CLIENT_OBJETS} ${LDLIBS} |
|||
|
|||
${SERVER}: depend-${SERVER} ${SERVER_OBJETS} |
|||
${CXX} -o $@ ${CXXFLAGS} ${LDFLAGS} ${SERVER_OBJETS} ${LDLIBS} |
|||
|
|||
clean: |
|||
-@$(RM) *.o depend-${CLIENT} depend-${SERVER} core 1>/dev/null 2>&1 |
|||
|
|||
clean-all: clean |
|||
-@$(RM) -${CLIENT} -${SERVER} 1>/dev/null 2>&1 |
|||
|
|||
tar: |
|||
tar cvf ${CLISERV}.tar.gz ${CLISERV_SOURCES} |
|||
|
|||
# Gestion des dependances : creation automatique des dependances en utilisant |
|||
# l'option -MM de g++ (attention tous les compilateurs n'ont pas cette option) |
|||
# |
|||
depend-${CLIENT}: |
|||
${CXX} ${CXXFLAGS} -MM ${CLIENT_SOURCES} > depend-${CLIENT} |
|||
|
|||
depend-${SERVER}: |
|||
${CXX} ${CXXFLAGS} -MM ${SERVER_SOURCES} > depend-${SERVER} |
|||
|
|||
########################################### |
|||
# |
|||
# Regles implicites |
|||
# |
|||
|
|||
.SUFFIXES: .cpp .cxx .c |
|||
|
|||
.cpp.o: |
|||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
.cxx.o: |
|||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
.c.o: |
|||
$(CC) -c (CFLAGS) $(INCPATH) -o $@ $< |
|||
|
|||
|
|||
############################################# |
|||
# |
|||
# Inclusion du fichier des dependances |
|||
# |
|||
-include depend-${CLIENT} |
|||
-include depend-${SERVER} |
@ -0,0 +1,35 @@ |
|||
#include "Photo.h" |
|||
#include "Base.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
using namespace std; |
|||
|
|||
Photo::Photo(std::string name, std::string pathname, unsigned int latitude, unsigned int longitude) : Base(name, pathname), latitude(latitude), longitude(longitude) {} |
|||
Photo::~Photo() {} |
|||
|
|||
void Photo::setLatitude(unsigned int _latitude) { |
|||
latitude = _latitude; |
|||
} |
|||
|
|||
void Photo::setLongitude(unsigned int _longitude) { |
|||
longitude = _longitude; |
|||
} |
|||
|
|||
unsigned int Photo::getLatitude() const { |
|||
return latitude; |
|||
} |
|||
|
|||
unsigned int Photo::getLongitude() const { |
|||
return longitude; |
|||
} |
|||
|
|||
void Photo::affichage(ostream& flux) const { |
|||
Base::affichage(flux); |
|||
flux << "La photo a pour latitude : " << latitude << " et pour longitude : " << longitude << endl; |
|||
} |
|||
|
|||
void Photo::play() const { |
|||
string command = "display " + pathname + " &"; |
|||
system(command.c_str()); |
|||
} |
@ -0,0 +1,35 @@ |
|||
#ifndef PHOTO_H |
|||
#define PHOTO_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include "Base.h" |
|||
|
|||
/**
|
|||
* @brief Classe des medias de type photo, héritant de Base |
|||
* |
|||
* Une photo possède deux attributs particuliers : une latitude et une longitude |
|||
* |
|||
* La classe comporte les getteurs et setteurs associés |
|||
* |
|||
* La fonction affichage affiche sur le flux les attributs |
|||
* |
|||
* La fonction play affiche la photo avec la commande @e display |
|||
*/ |
|||
|
|||
class Photo : public Base { |
|||
private: |
|||
unsigned int latitude{}; |
|||
unsigned int longitude{}; |
|||
public: |
|||
Photo(std::string, std::string, unsigned int = 0, unsigned int = 0); |
|||
virtual ~Photo(); |
|||
void setLatitude(unsigned int); |
|||
void setLongitude(unsigned int); |
|||
unsigned int getLatitude() const; |
|||
unsigned int getLongitude() const; |
|||
virtual void affichage(std::ostream&) const; |
|||
virtual void play() const; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,27 @@ |
|||
#include "Video.h" |
|||
#include "Base.h" |
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
using namespace std; |
|||
|
|||
Video::Video(string name, string pathname, unsigned int duree) : Base(name, pathname), duree(duree) {} |
|||
Video::~Video() {} |
|||
|
|||
void Video::setDuree(unsigned int _duree) { |
|||
duree = _duree; |
|||
} |
|||
|
|||
unsigned int Video::getDuree() const { |
|||
return duree; |
|||
} |
|||
|
|||
void Video::affichage(ostream& flux) const { |
|||
Base::affichage(flux); |
|||
flux << "La vidéo a pour durée : " << duree << endl; |
|||
} |
|||
|
|||
void Video::play() const { |
|||
string command = "mpv " + pathname + " &"; |
|||
system(command.c_str()); |
|||
} |
@ -0,0 +1,32 @@ |
|||
#ifndef VIDEO_H |
|||
#define VIDEO_H |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
#include "Base.h" |
|||
|
|||
/**
|
|||
* @brief Classe des medias de type vidéo, héritant de Base |
|||
* |
|||
* Une photo possède un attribut particulier : une durée |
|||
* |
|||
* La classe comporte le getteur et le setteur associés |
|||
* |
|||
* La fonction affichage affiche sur le flux l'attribut |
|||
* |
|||
* La fonction play affiche la vidéo avec la commande @e mpv |
|||
*/ |
|||
|
|||
class Video : public Base { |
|||
protected: |
|||
unsigned int duree{}; |
|||
public: |
|||
Video(std::string, std::string, unsigned int = 0); |
|||
virtual ~Video(); |
|||
void setDuree(unsigned int); |
|||
unsigned int getDuree() const; |
|||
virtual void affichage(std::ostream&) const; |
|||
virtual void play() const; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,469 @@ |
|||
//
|
|||
// ccsocket: C++ Classes for TCP/IP and UDP Datagram INET Sockets.
|
|||
// (c) Eric Lecolinet 2016/2020 - http://www.telecom-paristech.fr/~elc
|
|||
//
|
|||
|
|||
#include <iostream> |
|||
#include <cstring> |
|||
#include <cstdlib> |
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
#include <winsock2.h> |
|||
#include <ws2tcpip.h> |
|||
#pragma comment(lib, "ws2_32.lib") |
|||
|
|||
#else |
|||
#include <unistd.h> // fcntl.h won't compile without unistd.h ! |
|||
#include <netinet/tcp.h> |
|||
#include <netdb.h> |
|||
#endif |
|||
#include <fcntl.h> |
|||
#include <csignal> |
|||
#include "ccsocket.h" |
|||
|
|||
using namespace std; |
|||
|
|||
void Socket::startup() { |
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
static bool started = false; |
|||
static WSADATA WSAData; |
|||
if (!started) { |
|||
started = true; |
|||
WSAStartup(MAKEWORD(2, 0), &WSAData); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
void Socket::cleanup() { |
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
static bool started = false; |
|||
if (!started) { |
|||
started = true; |
|||
WSACleanup(); |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
Socket::Socket(int type) { |
|||
startup(); |
|||
|
|||
// family is AF_INET (AF_UNIX or AF_INET6 not supported)
|
|||
// type can be SOCK_STREAM (TCP/IP) or SOCK_DGRAM (datagram connection)
|
|||
// protocol is 0 (ie chosen automatically)
|
|||
sockfd_ = ::socket(AF_INET, type, 0); |
|||
|
|||
// ignore SIGPIPES when possible
|
|||
#if defined(SO_NOSIGPIPE) |
|||
int set = 1; |
|||
setsockopt(sockfd_, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
Socket::Socket(int, SOCKET sockfd) : sockfd_(sockfd) { |
|||
startup(); |
|||
} |
|||
|
|||
Socket::~Socket() { |
|||
close(); |
|||
} |
|||
|
|||
|
|||
// for INET4 sockets
|
|||
int Socket::setLocalAddress(SOCKADDR_IN& addr, int port) { |
|||
addr = {}; |
|||
addr.sin_family = AF_INET; |
|||
addr.sin_port = htons(port); |
|||
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
// for INET4 sockets
|
|||
int Socket::setAddress(SOCKADDR_IN& addr, const string& host, int port) { |
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
addr = {}; |
|||
addr.sin_family = AF_INET; |
|||
addr.sin_port = htons(port); |
|||
inet_pton(AF_INET, host.data(), &addr.sin_addr.s_addr); |
|||
#else |
|||
addr = {}; |
|||
struct hostent* hent = NULL; |
|||
// gethostbyname() is obsolete!
|
|||
if (host.empty() || !(hent = ::gethostbyname(host.c_str()))) return -1; // host not found
|
|||
addr.sin_family = AF_INET; |
|||
addr.sin_port = htons(port); |
|||
// NB: data might be misaligned but correct result because memcpy() is used
|
|||
::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length); |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int Socket::bind(int port) { |
|||
if (sockfd_ == INVALID_SOCKET) return InvalidSocket; |
|||
// for INET4 sockets
|
|||
SOCKADDR_IN addr; |
|||
setLocalAddress(addr, port); |
|||
// assigns the address specified by addr to sockfd (returns -1 on error, 0 otherwise)
|
|||
return ::bind(sockfd_, (const SOCKADDR*)&addr, sizeof(addr)); |
|||
} |
|||
|
|||
|
|||
int Socket::bind(const string& host, int port) { |
|||
if (sockfd_ == INVALID_SOCKET) return InvalidSocket; |
|||
// for INET4 sockets
|
|||
SOCKADDR_IN addr; |
|||
if (setAddress(addr, host, port) < 0) return UnknownHost; |
|||
// assigns the address specified by addr to sockfd (returns -1 on error, 0 otherwise)
|
|||
return ::bind(sockfd_, (const SOCKADDR*)&addr, sizeof(addr)); |
|||
} |
|||
|
|||
|
|||
int Socket::connect(const string& host, int port) { |
|||
if (sockfd_ == INVALID_SOCKET) return InvalidSocket; |
|||
// for INET4 sockets
|
|||
SOCKADDR_IN addr; |
|||
if (setAddress(addr, host, port) < 0) return UnknownHost; |
|||
// connects sockfd to the address specified by addr (returns -1 on error, 0 otherwise)
|
|||
return ::connect(sockfd_, (SOCKADDR*)&addr, sizeof(addr)); |
|||
} |
|||
|
|||
|
|||
int Socket::close() { |
|||
int stat = 0; |
|||
if (sockfd_ != INVALID_SOCKET) { |
|||
stat = ::shutdown(sockfd_, 2); // SHUT_RDWR=2
|
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
stat += ::closesocket(sockfd_); |
|||
#else |
|||
stat += ::close(sockfd_); |
|||
#endif |
|||
} |
|||
sockfd_ = INVALID_SOCKET; |
|||
return stat; |
|||
} |
|||
|
|||
|
|||
void Socket::shutdownInput() { |
|||
::shutdown(sockfd_, 0); |
|||
} |
|||
|
|||
void Socket::shutdownOutput() { |
|||
::shutdown(sockfd_, 1/*SD_SEND*/); |
|||
} |
|||
|
|||
#if !defined(_WIN32) && !defined(_WIN64) |
|||
|
|||
int Socket::setReceiveBufferSize(int size) { |
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int)); |
|||
} |
|||
|
|||
int Socket::setSendBufferSize(int size) { |
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int)); |
|||
} |
|||
|
|||
int Socket::setReuseAddress(bool state) { |
|||
int set = state; |
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int)); |
|||
} |
|||
|
|||
int Socket::setSoLinger(bool on, int time) { |
|||
struct linger l; |
|||
l.l_onoff = on; // Linger active
|
|||
l.l_linger = time; // How long to linger for
|
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); |
|||
} |
|||
|
|||
int Socket::setSoTimeout(int timeout) { |
|||
struct timeval tv; |
|||
tv.tv_sec = timeout / 1000; // ms to seconds
|
|||
tv.tv_usec = (timeout % 1000) * 1000; // ms to microseconds
|
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
|||
} |
|||
|
|||
int Socket::setTcpNoDelay(bool state) { |
|||
int set = state; |
|||
return ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, &set, sizeof(int)); |
|||
} |
|||
|
|||
#endif |
|||
|
|||
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|||
|
|||
|
|||
ServerSocket::ServerSocket() { |
|||
Socket::startup(); |
|||
sockfd_ = ::socket(AF_INET, SOCK_STREAM, 0); |
|||
} |
|||
|
|||
ServerSocket::~ServerSocket() { |
|||
close(); |
|||
} |
|||
|
|||
Socket* ServerSocket::createSocket(SOCKET sockfd) { |
|||
return new Socket(SOCK_STREAM, sockfd); |
|||
} |
|||
|
|||
|
|||
int ServerSocket::bind(int port, int backlog) { |
|||
if (sockfd_ == INVALID_SOCKET) return Socket::InvalidSocket; |
|||
|
|||
SOCKADDR_IN addr{}; |
|||
addr.sin_family = AF_INET; |
|||
addr.sin_port = htons(port); |
|||
addr.sin_addr.s_addr = INADDR_ANY; |
|||
|
|||
if (::bind(sockfd_, (SOCKADDR*)&addr, sizeof(addr)) < 0) return -1; |
|||
|
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
#else |
|||
socklen_t taille = sizeof addr; |
|||
if (::getsockname(sockfd_, (SOCKADDR*)&addr, &taille) < 0) return -1; |
|||
#endif |
|||
|
|||
// le serveur se met en attente sur le socket d'ecoute
|
|||
// listen s'applique seulement aux sockets de type SOCK_STREAM ou SOCK_SEQPACKET.
|
|||
if (::listen(sockfd_, backlog) < 0) return -1; |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int ServerSocket::close() { |
|||
int stat = 0; |
|||
if (sockfd_ != INVALID_SOCKET) { |
|||
// ::shutdown(sockfd, SHUT_RDWR);
|
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
::closesocket(sockfd_); |
|||
#else |
|||
::close(sockfd_); |
|||
#endif |
|||
} |
|||
sockfd_ = INVALID_SOCKET; |
|||
return stat; |
|||
} |
|||
|
|||
|
|||
Socket* ServerSocket::accept() { |
|||
SOCKET sock_com{}; |
|||
|
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
SOCKADDR_IN addr_com; |
|||
int sizeofaddr_ = sizeof(addr_com); |
|||
sock_com = ::accept(sockfd_, (SOCKADDR*)&addr_com, &sizeofaddr_); |
|||
if (sock_com == INVALID_SOCKET) return nullptr; |
|||
#else |
|||
// cf. man -s 3n accept, EINTR et EWOULBLOCK ne sont pas geres!
|
|||
if ((sock_com = ::accept(sockfd_, NULL, NULL)) < 0) return nullptr; |
|||
#endif |
|||
return createSocket(sock_com); |
|||
} |
|||
|
|||
#if !defined(_WIN32) && !defined(_WIN64) |
|||
|
|||
int ServerSocket::setReceiveBufferSize(int size) { |
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_RCVBUF, &size, sizeof(int)); |
|||
} |
|||
|
|||
|
|||
int ServerSocket::setReuseAddress(bool state) { |
|||
int set = state; |
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int)); |
|||
} |
|||
|
|||
|
|||
int ServerSocket::setSoTimeout(int timeout) { |
|||
struct timeval tv; |
|||
tv.tv_sec = timeout / 1000; // ms to seconds
|
|||
tv.tv_usec = (timeout % 1000) * 1000; // ms to microseconds
|
|||
return ::setsockopt(sockfd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
|||
} |
|||
|
|||
|
|||
int ServerSocket::setTcpNoDelay(bool state) { |
|||
// turn off TCP coalescence for INET sockets (useful in some cases to avoid delays)
|
|||
int set = state; |
|||
return ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char*)&set, sizeof(int)); |
|||
} |
|||
|
|||
#endif |
|||
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|||
|
|||
|
|||
struct InputBuffer { |
|||
InputBuffer(size_t size) : |
|||
buffer(new char[size]), |
|||
begin(buffer), |
|||
end(buffer + size), remaining(0) { |
|||
} |
|||
|
|||
~InputBuffer() { |
|||
delete[] buffer; |
|||
} |
|||
char* buffer; |
|||
char* begin; |
|||
char* end; |
|||
SOCKSIZE remaining; |
|||
}; |
|||
|
|||
|
|||
SocketBuffer::SocketBuffer(Socket* sock, size_t inSize, size_t outSize) : |
|||
insize_(inSize), |
|||
outsize_(outSize), |
|||
insep_(-1), // means '\r' or '\n' or "\r\n"
|
|||
outsep_('\n'), |
|||
sock_(sock), |
|||
in_(nullptr) { |
|||
} |
|||
|
|||
|
|||
SocketBuffer::SocketBuffer(Socket& sock, size_t inSize, size_t outSize) |
|||
: SocketBuffer(&sock, inSize, outSize) {} |
|||
|
|||
|
|||
SocketBuffer::~SocketBuffer() { |
|||
delete in_; |
|||
} |
|||
|
|||
|
|||
void SocketBuffer::setReadSeparator(int separ) { |
|||
insep_ = separ; |
|||
} |
|||
|
|||
|
|||
void SocketBuffer::setWriteSeparator(int separ) { |
|||
outsep_ = separ; |
|||
} |
|||
|
|||
|
|||
SOCKSIZE SocketBuffer::readLine(string& str) { |
|||
str.clear(); |
|||
if (!sock_) return Socket::InvalidSocket; |
|||
if (!in_) in_ = new InputBuffer(insize_); |
|||
|
|||
while (true) { |
|||
if (retrieveLine(str, in_->remaining)) return str.length() + 1; |
|||
// - received > 0: data received
|
|||
// - received = 0: nothing received (shutdown or empty message)
|
|||
// - received < 0: an error occurred
|
|||
SOCKSIZE received = sock_->receive(in_->begin, in_->end - in_->begin); |
|||
if (received <= 0) return received; // -1 (error) or 0 (shutdown)
|
|||
if (retrieveLine(str, received)) return str.length() + 1; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool SocketBuffer::retrieveLine(string& str, SOCKSIZE received) { |
|||
if (received <= 0 || in_->begin > in_->end) { |
|||
in_->begin = in_->buffer; |
|||
return false; |
|||
} |
|||
|
|||
// search for separator
|
|||
char* sep = nullptr; |
|||
int sepLen = 1; |
|||
|
|||
if (insep_ < 0) { // means: '\r' or '\n' or "\r\n"
|
|||
for (char* p = in_->begin; p < in_->begin + received; ++p) { |
|||
if (*p == '\n') { |
|||
sep = p; |
|||
sepLen = 1; |
|||
break; |
|||
} |
|||
else if (*p == '\r') { |
|||
sep = p; |
|||
if (p < in_->begin + received - 1 && *(p + 1) == '\n') { |
|||
sepLen = 2; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
else { |
|||
for (char* p = in_->begin; p < in_->begin + received; ++p) |
|||
if (*p == insep_) { |
|||
sep = p; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (sep) { |
|||
str.append(in_->begin, sep - in_->begin); |
|||
in_->remaining = received - (sep + sepLen - in_->begin); |
|||
in_->begin = sep + sepLen; |
|||
return true; |
|||
} |
|||
else { |
|||
str.append(in_->begin, received); |
|||
in_->begin = in_->buffer; |
|||
in_->remaining = 0; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
SOCKSIZE SocketBuffer::writeLine(const string& str) { |
|||
if (!sock_) return Socket::InvalidSocket; |
|||
|
|||
// a negature value of _outSep means that \r\n must be added
|
|||
size_t len = str.length() + (outsep_ < 0 ? 2 : 1); |
|||
|
|||
// if len is not too large, try to send everything in one block
|
|||
if (len <= outsize_) { |
|||
char* buf = (char*)malloc(len); |
|||
::memcpy(buf, str.c_str(), str.length()); |
|||
if (outsep_ >= 0) buf[len - 1] = char(outsep_); |
|||
else { |
|||
buf[len - 2] = '\r'; |
|||
buf[len - 1] = '\n'; |
|||
} |
|||
auto stat = write(buf, len); |
|||
delete buf; |
|||
return stat; |
|||
} |
|||
else { |
|||
SOCKSIZE sent = write(str.c_str(), str.length()); |
|||
char buf[] = {char(outsep_), 0}; |
|||
if (outsep_ >= 0) sent += sock_->send(buf, 1); |
|||
else sent += sock_->send("\r\n", 2); |
|||
return sent; |
|||
} |
|||
} |
|||
|
|||
|
|||
SOCKSIZE SocketBuffer::write(const char* s, size_t len) { |
|||
if (!sock_) return Socket::InvalidSocket; |
|||
const char* begin = s; |
|||
const char* end = s + len; |
|||
SOCKSIZE totalSent = 0; |
|||
|
|||
while (begin < end) { |
|||
// - sent > 0: data sent
|
|||
// - sent = 0: file was shutdown
|
|||
// - sent < 0: an error occurred
|
|||
SOCKSIZE sent = sock_->send(begin, end - begin); |
|||
if (sent <= 0) return sent; // -1 (error) or 0 (shutdown)
|
|||
begin += sent; |
|||
totalSent += sent; |
|||
} |
|||
return totalSent; |
|||
} |
|||
|
|||
|
|||
SOCKSIZE SocketBuffer::read(char* s, size_t len) { |
|||
if (!sock_) return Socket::InvalidSocket; |
|||
char* begin = s; |
|||
char* end = s + len; |
|||
SOCKSIZE totalReceived = 0; |
|||
|
|||
while (begin < end) { |
|||
SOCKSIZE received = sock_->receive(begin, end - begin); |
|||
if (received <= 0) return received; // -1 (error) or 0 (shutdown)
|
|||
begin += received; |
|||
totalReceived += received; |
|||
} |
|||
return totalReceived; |
|||
} |
@ -0,0 +1,353 @@ |
|||
//
|
|||
// ccsocket: C++ Classes for TCP/IP and UDP Datagram INET Sockets.
|
|||
// (c) Eric Lecolinet 2016/2020 - https://www.telecom-paris.fr/~elc
|
|||
//
|
|||
// - Socket: TCP/IP or UDP/Datagram IPv4 socket
|
|||
// - ServerSocket: TCP/IP Socket Server
|
|||
// - SocketBuffer: preserves record boundaries when exchanging data
|
|||
// between TCP/IP sockets.
|
|||
//
|
|||
|
|||
#ifndef ccuty_ccsocket |
|||
#define ccuty_ccsocket 1 |
|||
|
|||
#include <string> |
|||
|
|||
#if defined(_WIN32) || defined(_WIN64) |
|||
#include <winsock2.h> |
|||
#define SOCKSIZE int |
|||
#define SOCKDATA char |
|||
|
|||
#else |
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#define SOCKET int |
|||
#define SOCKADDR struct sockaddr |
|||
#define SOCKADDR_IN struct sockaddr_in |
|||
#define INVALID_SOCKET -1 |
|||
#define SOCKSIZE ssize_t |
|||
#define SOCKDATA void |
|||
#endif |
|||
|
|||
// ignore SIGPIPES when possible
|
|||
#if defined(MSG_NOSIGNAL) |
|||
# define NO_SIGPIPE_(flags) (flags | MSG_NOSIGNAL) |
|||
#else |
|||
# define NO_SIGPIPE_(flags) (flags) |
|||
#endif |
|||
|
|||
/** TCP/IP or UDP/Datagram IPv4 socket.
|
|||
* AF_INET connections following the IPv4 Internet protocol are supported. |
|||
* @note |
|||
* - ServerSocket should be used on the server side. |
|||
* - SIGPIPE signals are ignored when using Linux, BSD or MACOSX. |
|||
* - TCP/IP sockets do not preserve record boundaries but SocketBuffer solves this problem. |
|||
*/ |
|||
class Socket { |
|||
public: |
|||
/// Socket errors.
|
|||
/// - Socket::Failed (-1): could not connect, could not bind, etc.
|
|||
/// - Socket::InvalidSocket (-2): invalid socket or wrong socket type
|
|||
/// - Socket::UnknownHost (-3): could not reach host
|
|||
enum Errors { Failed = -1, InvalidSocket = -2, UnknownHost = -3 }; |
|||
|
|||
/// initialisation and cleanup of sockets on Widows.
|
|||
/// @note startup is automaticcaly called when a Socket or a ServerSocket is created
|
|||
/// @{
|
|||
static void startup(); |
|||
static void cleanup(); |
|||
/// @}
|
|||
|
|||
/// Creates a new Socket.
|
|||
/// Creates a AF_INET socket using the IPv4 Internet protocol. Type can be:
|
|||
/// - SOCK_STREAM (the default) for TCP/IP connected stream sockets
|
|||
/// - SOCK_DGRAM for UDP/datagram sockets (available only or Unix/Linux)
|
|||
Socket(int type = SOCK_STREAM); |
|||
|
|||
/// Creates a Socket from an existing socket file descriptor.
|
|||
Socket(int type, SOCKET sockfd); |
|||
|
|||
/// Destructor (closes the socket).
|
|||
~Socket(); |
|||
|
|||
/// Connects the socket to an address.
|
|||
/// Typically used for connecting TCP/IP clients to a ServerSocket.
|
|||
/// On Unix/Linux host can be a hostname, on Windows it can only be an IP address.
|
|||
/// @return 0 on success or a negative value on error which is one of Socket::Errors
|
|||
int connect(const std::string& host, int port); |
|||
|
|||
/// Assigns the socket to localhost.
|
|||
/// @return 0 on success or a negative value on error, see Socket::Errors
|
|||
int bind(int port); |
|||
|
|||
/// Assigns the socket to an IP address.
|
|||
/// On Unix/Linux host can be a hostname, on Windows it can only be an IP address.
|
|||
/// @return 0 on success or a negative value on error, see Socket::Errors
|
|||
int bind(const std::string& host, int port); |
|||
|
|||
/// Closes the socket.
|
|||
int close(); |
|||
|
|||
/// Returns true if the socket has been closed.
|
|||
bool isClosed() const { return sockfd_ == INVALID_SOCKET; } |
|||
|
|||
/// Returns the descriptor of the socket.
|
|||
SOCKET descriptor() { return sockfd_; } |
|||
|
|||
/// Disables further receive operations.
|
|||
void shutdownInput(); |
|||
|
|||
/// Disables further send operations.
|
|||
void shutdownOutput(); |
|||
|
|||
/// Send sdata to a connected (TCP/IP) socket.
|
|||
/// Sends the first _len_ bytes in _buf_.
|
|||
/// @return the number of bytes that were sent, or 0 or shutdownInput() was called on the other side,
|
|||
/// or Socket::Failed (-1) if an error occured.
|
|||
/// @note TCP/IP sockets do not preserve record boundaries, see SocketBuffer.
|
|||
SOCKSIZE send(const SOCKDATA* buf, size_t len, int flags = 0) { |
|||
return ::send(sockfd_, buf, len, NO_SIGPIPE_(flags)); |
|||
} |
|||
|
|||
/// Receives data from a connected (TCP/IP) socket.
|
|||
/// Reads at most _len_ bytes fand stores them in _buf_.
|
|||
/// By default, this function blocks the caller until thre is availbale data.
|
|||
/// @return the number of bytes that were received, or 0 or shutdownOutput() was called on the
|
|||
/// other side, or Socket::Failed (-1) if an error occured.
|
|||
SOCKSIZE receive(SOCKDATA* buf, size_t len, int flags = 0) { |
|||
return ::recv(sockfd_, buf, len, flags); |
|||
} |
|||
|
|||
#if !defined(_WIN32) && !defined(_WIN64) |
|||
|
|||
/// Sends data to a datagram socket.
|
|||
SOCKSIZE sendTo(void const* buf, size_t len, int flags, |
|||
SOCKADDR const* to, socklen_t addrlen) { |
|||
return ::sendto(sockfd_, buf, len, NO_SIGPIPE_(flags), to, addrlen); |
|||
} |
|||
|
|||
/// Receives data from datagram socket.
|
|||
SOCKSIZE receiveFrom(void* buf, size_t len, int flags, |
|||
SOCKADDR* from, socklen_t* addrlen) { |
|||
return ::recvfrom(sockfd_, buf, len, flags, from, addrlen); |
|||
} |
|||
|
|||
/// Set the size of the TCP/IP input buffer.
|
|||
int setReceiveBufferSize(int size); |
|||
|
|||
/// Enable/disable the SO_REUSEADDR socket option.
|
|||
int setReuseAddress(bool); |
|||
|
|||
/// Set the size of the TCP/IP output buffer.
|
|||
int setSendBufferSize(int size); |
|||
|
|||
/// Enable/disable SO_LINGER with the specified linger time in seconds.
|
|||
int setSoLinger(bool, int linger); |
|||
|
|||
/// Enable/disable SO_TIMEOUT with the specified timeout (in milliseconds).
|
|||
int setSoTimeout(int timeout); |
|||
|
|||
/// Enable/disable TCP_NODELAY (turns on/off TCP coalescence).
|
|||
int setTcpNoDelay(bool); |
|||
|
|||
/// Return the size of the TCP/IP input buffer.
|
|||
int getReceiveBufferSize() const; |
|||
|
|||
/// Return SO_REUSEADDR state.
|
|||
bool getReuseAddress() const; |
|||
|
|||
/// Return the size of the TCP/IP output buffer.
|
|||
int getSendBufferSize() const; |
|||
|
|||
/// Return SO_LINGER state and the specified linger time in seconds.
|
|||
bool getSoLinger(int& linger) const; |
|||
|
|||
/// Return SO_TIMEOUT value.
|
|||
int getSoTimeout() const; |
|||
|
|||
/// Return TCP_NODELAY state.
|
|||
bool getTcpNoDelay() const; |
|||
|
|||
#endif |
|||
|
|||
private: |
|||
friend class ServerSocket; |
|||
|
|||
// Initializes a local INET4 address, returns 0 on success, -1 otherwise.
|
|||
int setLocalAddress(SOCKADDR_IN& addr, int port); |
|||
// Initializes a remote INET4 address, returns 0 on success, -1 otherwise.
|
|||
int setAddress(SOCKADDR_IN& addr, const std::string& host, int port); |
|||
|
|||
SOCKET sockfd_{}; |
|||
Socket(const Socket&) = delete; |
|||
Socket& operator=(const Socket&) = delete; |
|||
Socket& operator=(Socket&&) = delete; |
|||
}; |
|||
|
|||
|
|||
|
|||
/// TCP/IP IPv4 server socket.
|
|||
/// Waits for requests to come in over the network.
|
|||
/// TCP/IP sockets do not preserve record boundaries but SocketBuffer solves this problem.
|
|||
class ServerSocket { |
|||
public: |
|||
/// Creates a listening socket that waits for connection requests by TCP/IP clients.
|
|||
ServerSocket(); |
|||
|
|||
~ServerSocket(); |
|||
|
|||
/// Accepts a new connection request and returns a socket for exchanging data with this client.
|
|||
/// This function blocks until there is a connection request.
|
|||
/// @return the new Socket or nullptr on error.
|
|||
Socket* accept(); |
|||
|
|||
/// Assigns the server socket to localhost.
|
|||
/// @return 0 on success or a negative value on error, see Socket::Errors
|
|||
int bind(int port, int backlog = 50); |
|||
|
|||
/// Closes the socket.
|
|||
int close(); |
|||
|
|||
/// Returns true if the socket was closed.
|
|||
bool isClosed() const { return sockfd_ == INVALID_SOCKET; } |
|||
|
|||
/// Returns the descriptor of the socket.
|
|||
SOCKET descriptor() { return sockfd_; } |
|||
|
|||
#if !defined(_WIN32) && !defined(_WIN64) |
|||
|
|||
/// Sets the SO_RCVBUF option to the specified value.
|
|||
int setReceiveBufferSize(int size); |
|||
|
|||
/// Enables/disables the SO_REUSEADDR socket option.
|
|||
int setReuseAddress(bool); |
|||
|
|||
/// Enables/disables SO_TIMEOUT with the specified timeout (in milliseconds).
|
|||
int setSoTimeout(int timeout); |
|||
|
|||
/// Turns on/off TCP coalescence (useful in some cases to avoid delays).
|
|||
int setTcpNoDelay(bool); |
|||
|
|||
#endif |
|||
|
|||
private: |
|||
Socket* createSocket(SOCKET); |
|||
SOCKET sockfd_{}; // listening socket.
|
|||
ServerSocket(const ServerSocket&) = delete; |
|||
ServerSocket& operator=(const ServerSocket&) = delete; |
|||
ServerSocket& operator=(ServerSocket&&) = delete; |
|||
}; |
|||
|
|||
|
|||
/** Preserves record boundaries when exchanging messages between connected TCP/IP sockets.
|
|||
* Ensures that one call to readLine() corresponds to one and exactly one call to writeLine() on the other side. |
|||
* By default, writeLine() adds \n at the end of each message and readLine() searches for \n, \r or \n\r |
|||
* so that it can retreive the entire record. Beware messages should thus not contain these charecters. |
|||
* |
|||
* @code |
|||
* int main() { |
|||
* Socket sock; |
|||
* SocketBuffer sockbuf(sock); |
|||
* |
|||
* int status = sock.connect("localhost", 3331); |
|||
* if (status < 0) { |
|||
* cerr << "Could not connect" << endl; |
|||
* return 1; |
|||
* } |
|||
* |
|||
* while (cin) { |
|||
* string request, response; |
|||
* cout << "Request: "; |
|||
* getline(cin, request); |
|||
* |
|||
* if (sockbuf.writeLine(request) < 0) { |
|||
* cerr << "Could not send message" << endl; |
|||
* return 2; |
|||
* } |
|||
* if (sockbuf.readLine(response) < 0) { |
|||
* cerr << "Couldn't receive message" << endl; |
|||
* return 3; |
|||
* } |
|||
* } |
|||
* return 0; |
|||
* } |
|||
@endcode |
|||
*/ |
|||
class SocketBuffer { |
|||
public: |
|||
/// Constructor.
|
|||
/// _socket_ must be a connected TCP/IP Socket. It should **not** be deleted as long as the
|
|||
/// SocketBuffer is used.
|
|||
/// _inputSize_ and _ouputSize_ are the sizes of the buffers that are used internally for exchanging data.
|
|||
/// @{
|
|||
SocketBuffer(Socket*, size_t inputSize = 8192, size_t ouputSize = 8192); |
|||
SocketBuffer(Socket&, size_t inputSize = 8192, size_t ouputSize = 8192); |
|||
/// @}
|
|||
|
|||
~SocketBuffer(); |
|||
|
|||
/** Read a message from a connected socket.
|
|||
* readLine() receives one (and only one) message sent by writeLine() on the other side, |
|||
* ie, a call to writeLine() corresponds to one and exactly one call to readLine() on the other side. |
|||
* The received data is stored in _message_. This method blocks until the message is fully received. |
|||
* |
|||
* @return The number of bytes that were received or one of the following values: |
|||
* - 0: shutdownOutput() was called on the other side |
|||
* - Socket::Failed (-1): a connection error occured |
|||
* - Socket::InvalidSocket (-2): the socket is invalid. |
|||
* @note the separator (eg \n) is counted in the value returned by readLine(). |
|||
*/ |
|||
SOCKSIZE readLine(std::string& message); |
|||
|
|||
/** Send a message to a connected socket.
|
|||
* writeLine() sends a message that will be received by a single call of readLine() on the other side, |
|||
* |
|||
* @return see readLine() |
|||
* @note if _message_ contains one or several occurences of the separator, readLine() will be |
|||
* called as many times on the other side. |
|||
*/ |
|||
SOCKSIZE writeLine(const std::string& message); |
|||
|
|||
/// Reads exactly _len_ bytes from the socket, blocks otherwise.
|
|||
/// @return see readLine()
|
|||
SOCKSIZE read(char* buffer, size_t len); |
|||
|
|||
/// Writes _len_ bytes to the socket.
|
|||
/// @return see readLine()
|
|||
SOCKSIZE write(const char* str, size_t len); |
|||
|
|||
/// Returns the associated socket.
|
|||
Socket* socket() { return sock_; } |
|||
|
|||
/// Returns/changes the separator used by readLine().
|
|||
/// setReadSeparator() changes the symbol used by readLine() to separate successive messages:
|
|||
/// - if _separ_ < 0 (the default) readLine() searches for \\n, \\r or \\n\\r.
|
|||
/// - if _separ_ >= 0, readLine() searches for this character to separate messages,
|
|||
/// @{
|
|||
void setReadSeparator(int separ); |
|||
int readSeparator() const { return insep_; } |
|||
// @}
|
|||
|
|||
/// Returns/changes the separator used by writeLine().
|
|||
/// setWriteSeparator() changes the character(s) used by writeLine() to separate successive messages:
|
|||
/// - if _separ_ < 0 (the default) writeLine() inserts \\n\\r between successive lines.
|
|||
/// - if _separ_ >= 0, writeLine() inserts _separ_ between successive lines,
|
|||
/// @{
|
|||
void setWriteSeparator(int separ); |
|||
int writeSeparator() const { return outsep_; } |
|||
// @}
|
|||
|
|||
private: |
|||
SocketBuffer(const SocketBuffer&) = delete; |
|||
SocketBuffer& operator=(const SocketBuffer&) = delete; |
|||
SocketBuffer& operator=(SocketBuffer&&) = delete; |
|||
|
|||
protected: |
|||
bool retrieveLine(std::string& str, SOCKSIZE received); |
|||
size_t insize_{}, outsize_{}; |
|||
int insep_{}, outsep_{}; |
|||
Socket* sock_{}; |
|||
struct InputBuffer* in_{}; |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,68 @@ |
|||
//
|
|||
// Client C++ pour communiquer avec un serveur TCP
|
|||
// eric lecolinet - telecom paristech - 2016/2020
|
|||
//
|
|||
|
|||
#include <iostream> |
|||
#include <string> |
|||
#include <algorithm> |
|||
# |