| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- from __future__ import unicode_literals
- # -*- coding: utf-8 -*-
- """serveurs chat et chat vocal (TCP et UDP)"""
- from socket import socket, AF_INET, SOCK_DGRAM, SOCK_STREAM
- from threading import Thread
- from os import system
- from time import time, sleep
- import logging
- #### intialisation des variables ####
- hote = ''
- port = 6660
- connectes = {} #id, adresse
- pseudos = {} #id, pseudo
- taille_paquet = 1024
- tampon = []
- #parametres deboguage
- #logging.basicConfig(level=logging.DEBUG,
- # format='(%(threadName)-10s) %(message)s',
- # )
- #gestion des erreurs et log
- logging.basicConfig(level=logging.DEBUG)
- logServeur = logging.getLogger(__name__)
- handlerServeur = logging.FileHandler('serveur.log')
- handlerServeur.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s - %(message)s')) #%(name)s - nom du module
- logServeur.addHandler(handlerServeur)
- logServeur.debug(" ---------------------- \n")
- #classes utilitaires:
- class OutilsCo():
- """outils utilises pour gerer la connexion"""
- def __init__(self):
- """initialisation"""
- self.idUtilises = ["00", "mj"]
-
- def genererID(self):
- """génère un id de 2 caractères non utilisé"""
- caracteres = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
- "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
- "<", ">", "+", "=", "-", "*", "@"]
- fin = False
-
- for c1 in caracteres:
- for c2 in caracteres:
- txt = "{}{}".format(c1,c2)
- if not txt in self.idUtilises:
- fin = True
- break
- if fin:
- break
- self.idUtilises.append(str(txt))
- logServeur.info("Id {} attribue".format(txt))
- return txt
-
- def libererID(self, txt):
- """retire un identifiant de la liste des id utilises"""
- try:
- self.idUtilises.remove(txt)
- except:
- logServeur.warning("impossible de liberer l'id {}".format(txt) )
- #### fils secondaires ####
- class DebitMetre(Thread):
- """debitmetre permettant de mesurer le debit d'emission/reception du serveur"""
- def __init__(self, taille_paquet):
- """création du serveur vocal"""
- Thread.__init__(self)
- self.mesureEnvoi = 0
- self.mesureReception = 0
- self.debitEmission = 0.00
- self.debitReception = 0.00
- #si oui, affiche les resultats chaque seconde:
- self.affichage = False
-
- def envoi(self, taille_paquet):
- """pour signaler l'envoi d'un paquet"""
- self.mesureEnvoi += taille_paquet
- def reception(self, taille_paquet):
- """pour signaler la reception d'un paquet"""
- self.mesureReception += taille_paquet
- def run(self):
- """mesure le volume en octets qui transite chaque seconde"""
- self.lance = True
- t0 = time()
- while self.lance:
- delta = time() - t0
- if delta >= 1:
- self.debitEmission = self.mesureEnvoi / delta
- self.mesureEnvoi = 0
- self.debitReception = self.mesureReception / delta
- self.mesureReception = 0
- t0 = time()
- if self.affichage:
- logServeur.info(self)
- def __repr__(self):
- """affiche les derniers debits mesures (en ko/s)"""
- retour = "** Reception: {} ko/s ** -> Emission: {} ko/s **"\
- "".format((self.debitReception * 0.001), (self.debitEmission * 0.001))
- return retour
- def stop(self):
- """Fermeture du fil debitmetre"""
- self.lance = False
-
- class ServeurVoc(Thread):
- """serveur UDP dedie au chat vocal"""
- def __init__(self, port):
- """création du serveur vocal"""
- Thread.__init__(self)
- self.serveur_lance = False
- self.connectes = []
- self.port = port
-
- def creer(self):
- """creation d'une entree serveur UDP"""
- try:
- self.socket = socket(AF_INET, SOCK_DGRAM)
- self.socket.bind(('', self.port))
- retour = "Le serveur vocal ecoute a present sur le port {}".format(self.port)
- self.serveur_lance = True
- self.start()
- except IOError, e:
- if e.errno==10048:
- retour = "Une instance du serveur est déja lancée"
- logging.error(retour)
- else:
- retour = "Echec de lancement du serveur"
- logServeur.error(retour)
- finally:
- logServeur.info(retour)
- return retour
-
- def nouveauClient(self, adresse):
- """ajoute un nouveau client a la liste des connectes"""
- self.connectes.append(adresse)
- retour = "Nouveau client connecte: {})".format(adresse)
- logServeur.info(retour)
- return retour
-
- def supprimerClient(self, adresse):
- """ajoute un nouveau client a la liste des connectes"""
- self.connectes.remove(adresse)
- retour = "Client deconnecte: {})".format(adresse)
- logServeur.info(retour)
- return retour
- def run(self):
- """boucle de fonctionnement du serveur"""
- self.socket.setblocking(0)
- while self.serveur_lance:
- data = ""
- #on recoit les donnees envoyees
- try:
- data, adresse = self.socket.recvfrom(1024)
- try:
- dM.reception(len(data))
- except:
- pass
-
- except IOError, e:
- if e.errno == 10035:
- #pas de donnee recue
- pass
- elif e.errno == 10022:
- logServeur.debug("erreur de reception {}".format(adresse))
- sleep(0.02)
- elif e.errno == 10054:
- self.connectes = []
- logServeur.warning("reinitialisation de la liste des connectes")
- else:
- logServeur.error("erreur {}".format(e.errno))
- #self.supprimerClient()
- if len(data) > 0:
- if not adresse in self.connectes:
- #l'adresse n'est pas dans la liste: on l'ajoute aux connectes
- self.nouveauClient(adresse)
-
- for dest in self.connectes:
- if dest != adresse:
- #le client ne recoit pas ses propres donnees
- self.socket.sendto(data , dest)
- try:
- dM.envoi(len(data))
- except:
- pass
- #tmp = "{} -> {}".format(len(data), adresse)
-
- def stop(self):
- """Fermeture du serveur"""
- self.serveur_lance = False
- self.socket.close()
- retour = "Serveur vocal ferme"
- logServeur.info("Fermeture du serveur vocal")
- return retour
-
- class FilClient(Thread):
- """fil de connexion client au serveur TCP"""
- def __init__(self, outils, cnn):
- """creation du fil"""
- Thread.__init__(self)
- self.cnn = cnn
- self.pseudo = "inconnu"
- self.idClient = "00"
- self.outils = outils
- self.connecte = False
- def creer(self):
- """initialise la connexion avec le client"""
- self.connecte = True
- #on genere un id pour lui
- self.idClient = self.outils.genererID()
- #on ajoute son adresse reseau a la liste des connectes
- connectes[self.idClient] = self.cnn
- logServeur.info("Client {} connecte - Id {}".format(self.getName(), self.idClient))
- #on demarre le fil de reception
- self.start()
-
- def run(self):
- """echange avec le client"""
- self.cnn.setblocking(0)
- while self.connecte:
- #reception du message client
- try:
- msgClient = self.cnn.recv(1024)
- try:
- dM.reception(len(msgClient))
- except:
- pass
-
- tampon.append((self.getName(), msgClient))
- except IOError, e:
- if e.errno == 10035:
- #aucun message recu
- pass
- else:
- if self.connecte:
- logServeur.warning("serveur : erreur reception")
- self.stop()
- def stop(self):
- """ferme le fil client"""
- self.connecte = False
- #fermeture de la connexion avec le client:
- #on supprime le client de la liste des connectes
- sleep(0.01)
- try:
- del connectes[self.idClient]
- except:
- logServeur.warning("Impossible de retirer {} de la liste des connectes".format(self.idClient))
- #on libere son identifiant
- self.outils.libererID(self.idClient)
- self.cnn.close()
- logServeur.info("Client {} deconnecte ({})".format(self.pseudo, self.getName()))
-
- # Le fil se termine ici
- class Serveur(Thread):
- """serveur TCP pour chat, echange de fichiers ou d'objets..."""
- def __init__(self, port):
- """creation du serveur"""
- self.serveur_lance = False
- Thread.__init__(self)
- self.port = port
- self.outils = OutilsCo()
- self.filsOuverts = {}
- def traitement(self, nomClient, msg):
- """traite le message selon son type et son/ses destinataire(s)"""
- client = self.filsOuverts[nomClient]
- if len(msg) >= 6:
- nature = msg[0:2]
- emet = msg[2:4]
- dest = msg[4:6]
- try:
- contenu = msg[6:]
- except:
- contenu = ""
- if dest == "sa":
- #message pour le serveur, on ne le retransmet pas
- if nature == "ci":
- #le client informe de sa connexion et demande un identifiant
- client.pseudo = contenu
- pseudos[client.idClient] = client.pseudo
- logServeur.info("connexion: {} -> {} ({})".format(client.getName(), client.pseudo, client.idClient))
- client.cnn.sendall("cisa"+client.idClient)
- for cnn in connectes.values():
- if cnn != client.cnn:
- msg = "ccsatc{}{}".format(client.idClient, client.pseudo)
- cnn.sendall(msg)
- sleep(0.001)
- try:
- dM.envoi(len(msg))
- except:
- pass
-
- if nature == "cd":
- #client informe de sa deconnexion
- for cnn in connectes.values():
- if cnn != client.cnn:
- msg = "cdsaac{}".format(emet)
- cnn.sendall(msg)
- try:
- dM.envoi(len(msg))
- except:
- pass
- client.stop()
-
- elif nature == "f0":
- #client demande un identifiant pour un envoi de fichier
- msg = "f0sa{}{}".format(client.idClient, self.outils.genererID())
- client.cnn.sendall(msg)
-
- elif nature == "f1":
- #client signale au serveur qu'il peut liberer l'identifiant du fichier
- self.outils.libererID(contenu)
-
- elif dest == "tc":
- #message pour tous les clients, emetteur compris
- for cnn in connectes.values():
- cnn.sendall(msg)
- try:
- dM.envoi(len(msg)+1)
- except:
- pass
-
- elif dest == "ac":
- #message pour tous les clients excepte l'emetteur
- for cnn in connectes.values():
- if cnn != client.cnn:
- cnn.sendall(msg)
- try:
- dM.envoi(len(msg)+1)
- except:
- pass
-
-
- elif dest == "sv":
- #message pour le serveur vocal
- logServeur.debug("pour serveur vocal: {}".format(contenu))
- #else:
- elif dest in connectes:
- #message pour un client unique (mj ou autre)
- connectes[dest].sendall(msg)
- else:
- logServeur.warning("Erreur : format du message illisible -> {}".format(msg))
-
- def creer(self):
- """demarre le serveur"""
- """ try:"""
- self.cnn = socket(AF_INET, SOCK_STREAM)
- self.cnn.bind((hote, self.port))
- self.cnn.listen(5)
- retour = "Le serveur ecoute a present sur le port {}".format(port)
- self.serveur_lance = True
- self.start()
- """except:
- retour = "Impossible de demarrer le serveur"""
- """finally:
- logServeur.info(retour)
- """
- return self.serveur_lance, retour
- def run(self):
- """boucle de fonctionnement"""
- self.cnn.setblocking(0)
- while self.serveur_lance:
- try:
- #tant que le serveur est lance, il reste disponible a de nouvelles connexions
- cnnClient, adresse = self.cnn.accept()
- #on cree un nouveau fil pour le nouveau client
- client = FilClient(self.outils, cnnClient)
- #on ajoute ce fil à la liste pour pouvoir le fermer
- self.filsOuverts[client.getName()] = client
- client.creer()
-
- except IOError, e:
- if e.errno == 10035:
- #si pas de nouvelles connexions, on traite les messages en attente:
- #le serveur lit les 3 premiers car indiquant la longueur du message ('drapeaux' non compris)
- #puis les retire du message avant le traitement
- try:
- nomClient, txt = tampon[0]
- msgClient = txt
- if len(msgClient) >= 9:
- while len(msgClient) > 0:
- lg = int(msgClient[0:3]) + 6
- self.traitement(nomClient, msgClient[3:lg+3])
- msgClient = msgClient[lg+3:]
- tampon.remove((nomClient, txt))
- txt = msgClient = ""
- except IndexError:
- #la liste tampon est vide
- pass
-
- else:
- logServeur.error("serveur : erreur attente clients")
- break
-
- def stop(self):
- """ferme le serveur"""
- self.serveur_lance = False
- #for client in connectes.keys():
- for client in self.filsOuverts.values():
- if client.connecte:
- client.cnn.sendall("sdsatc")
- client.stop()
- sleep(0.1)
- self.cnn.close()
- logging.info("Fermeture du serveur TCP")
-
- #### fil principal ####
- if __name__ == "__main__":
- # si lancement direct:
- #debitmetre pour mesurer l'emission
- #dM = DebitMetre(taille_paquet)
- #dM.affichage = False
-
- s = Serveur(port)
-
- s.creer()
- sVoc = ServeurVoc(port)
- sVoc.creer()
- #dM.start()
-
- system("pause")
- s.stop()
- sVoc.stop()
- #dM.stop()
- logServeur.info("- Fin -")
- sleep(0.1)
-
-
-
-
-
|