servidor de chat, uma vez que nao precisaremos inventar um delimitador de mensagem, nem separar as mensagens manualmente em nosso codigo.Em relacao a nosso servidor de chat, o servidor de exemplo e bem limitado. A maior limitacao e nao permitir a comunicacao entre clientes. A ideia do servidor de chat e enviar mensagens a todos os clientes conectados. Desta forma, o servidor deve ser informado sobre e registrar todas as conexoes e desconexoes do sistema. Vamos manter a lista dos clientes conectados com uma lista. Nossos clientes serao controlados por uma classe Cliente que veremos mais tarde. Observe a implementacao parcial da classe Servidor:class Servidor: def __init__(self): self.conectados = [] @property def nconectados(self): return len(self.conectados) @asyncio.coroutine def conecta(self, websocket, path): cliente = Cliente(self, websocket, path) if cliente not in self.conectados: self.conectados.append(cliente) print("Novo cliente conectado. Total: {0}".format(self.nconectados)) yield from cliente.gerencia() def desconecta(self, cliente): if cliente in self.conectados: self.conectados.remove(cliente) print("Cliente {1} desconectado. Total: {0}".format(self.nconectados, cliente.nome))Veja que apenas conecta e uma corotina. Na realidade, todo o trabalho e feito na classe Cliente, que disponibiliza o metodo gerencia como uma corotina. O importante agora e entender a manutencao da lista de conexoes ativas.Vejamos a classe Cliente (parcial):class Cliente: def __init__(self, servidor, websocket, path): self.cliente = websocket self.servidor = servidor self.nome = None @property def conectado(self): return self.cliente.open @asyncio.coroutine def gerencia(self): try: yield from self.envia("Bem vindo ao servidor de chat escrito em Python 3.4 com asyncio e WebSockets. Identifique-se com /nome SeuNome") while True: mensagem = yield from self.recebe() if mensagem: print("{0} < {1}".format(self.nome, mensagem)) yield from self.processa_comandos(mensagem) else: break except Exception: print("Erro") raise finally: self.servidor.desconecta(self)Como cada cliente tem seu proprio websocket e precisa se comunicar com o servidor, guardaremos estas referencias como atributos. Ja preparamos tambem a gestao de nomes, embora tenhamos inicializado o nome do Cliente com None. O metodo gerencia, que e uma corotina, envia uma mensagem de boas vindas ao cliente e como no exemplo anterior, utiliza yield from para realizar o envio no loop de eventos. Uma vez que a mensagem inicial e enviada, entramos em um loop infinito que espera uma mensagem do cliente. Quando a conexao e fechada ou acontece um erro, o valor de mensagem e igual a None, por isso, testamos o valor de mensagem para sair do loop infinito criado pelo while True. Da mesma forma que no primeiro servidor de exemplo, nosso cliente e desconectado quando a corotina gerencia termina. Aproveitamos o fim da corotina para informar ao servidor que este cliente esta se desconectado.Ao recebermos uma mensagem, iniciamos o processamento da mesma, utilizando o metodo corotina processa_comandos da classe Cliente: @asyncio.coroutine def processa_comandos(self, mensagem): if mensagem.strip().startswith("/"): comandos=shlex.split(mensagem.strip()[1:]) if len(comandos)==0: yield from self.envia("Comando invalido") return print(comandos) comando = comandos[0].lower() if comando == "horas": yield from self.envia("Hora atual: " + time.strftime("%H:%M:%S")) elif comando == "data": yield from self.envia("Data atual: " + time.strftime("%d/%m/%y")) elif comando == "clientes": yield from self.envia("{0} clientes conectados".format(self.servidor.nconectados)) elif comando == "nome": yield from self.altera_nome(comandos) elif comando == "apenas": yield from self.apenas_para(comandos) else: yield from self.envia("Comando desconhecido") else: if self.nome: yield from self.servidor.envia_a_todos(self, mensagem) else: yield from self.envia("Identifique-se para enviar mensagens. Use o comando