nosso servidor. Para desativa-lo, digite CTRL+C.Neste ponto, o endereco 127.0.0.1, porta 8888 estara recebendo conexoes. Quando uma conexao for recebida, uma nova instancia de EchoServer sera criada. Ao se estabelecer a conexao o metodo connection_made sera chamado e ativara uma corotina gerencia para gerenciar a recepcao e o envio de linhas de comandos. O metodo data_received e chamado sempre que novos dados forem recebidos (seja uma linha completa ou nao). O metodo connection_lost e chamado quando o cliente se desconectar.Vejamos o codigo fonte de common.py:import stringdef byteToHex(data, sep=" "): return sep.join("{0:02X}".format(x) for x in data)def strPrintable(data, sep=" "): return sep.join("{0:2s}".format(chr(s) if chr(s) in string.printable and s>30 else ".") for s in data)def strToByte(s, encoding ="utf-8"): return s.encode(encoding)def byteToStr(data, encoding ="utf-8"): return data.decode(encoding, errors="replace")E o codigo fonte de nosso cliente.py:import asyncioimport timefrom common import *class EchoClient(asyncio.streams.FlowControlMixin): def connection_made(self, transporte): peername = transporte.get_extra_info('peername') print('Conectado a {}'.format(peername)) self.transporte = transporte self.leitor = asyncio.StreamReader() self.leitor.set_transport(self.transporte) self.escritor = asyncio.StreamWriter(transport=self.transporte, protocol=self, reader=self.leitor, loop=asyncio.get_event_loop()) asyncio.async(self.gerencia()) self.feito = asyncio.Future() @asyncio.coroutine def gerencia(self): for x in range(10): self.escritor.write(strToByte("Alo\r\n")) yield from self.escritor.drain() dados = yield from self.leitor.readline() self.escritor.write(strToByte("sair\r\n")) yield from self.escritor.drain() self.transporte.close() self.feito.set_result(True) def connection_lost(self, exp): print("Conexao perdida") super().connection_lost(exp) def data_received(self, dados): print('dados recebidos: {0}'.format(byteToHex(dados))) print(' recebidos: {0}'.format(strPrintable(dados))) print(' string: {0}'.format(byteToStr(dados))) self.leitor.feed_data(dados) loop = asyncio.get_event_loop()coro = loop.create_connection(EchoClient, '127.0.0.1', 8888)transporte, protocolo = loop.run_until_complete(coro)try: loop.run_until_complete(protocolo.feito)except KeyboardInterrupt: passfinally: loop.close()Execute o servidor e depois o cliente, cada em uma janela ou terminal diferente. Veja que o cliente termina sua execucao apos enviar 10 vezes o comando Alo e sair. Execute varias vezes o cliente e veja que o servidor continua ativo. Experimente aumentar o numero de comandos de 10 para 100 no cliente e reexecute. Tente executar a partir de uma terceira janela outro cliente simultaneamente. Observe que conseguimos implementar um cliente e um servidor TCP/IP em um pouco mais de 100 linhas de codigo em Python. Um servidor capaz de atender varios clientes sem utilizar multiplos threads.Voce pode comentar ou remover os prints que nao precisar, eles servem apenas para debugar.O codigo do cliente e muito parecido com o codigo do servidor. A principal mudanca e o metodo gerencia e o atributo self.feito, criado com asyncio.Future(). Vejamos a criacao da instancia de nosso cliente. A funcao create_conection e na realidade uma corotina que recebe o protocolo (no caso EchoClient), o ip e a porta do servidor. Ao chamarmos loop.run_until_complete(coro), uma tupla com o transporte e o protocolo e retornada. Este retorno e importante, pois precisamos ter acesso a instancia de EchoClient criada para gerenciar nossa conexao, no caso protocolo. Com a instancia de EchoClient retornada em protocolo, podemos rodar o loop ate que feito seja marcada como finalizada: loop.run_until_complete(protocolo.feito). Esta etapa e importante, pois devemos executar o loop ate que o cliente tenha tempo para terminar seu trabalho. Veja que no final de gerencia, marcamos self.feito como concluida: self.feito.set_result(True).Se voce quiser testar o servidor com varios clientes simultaneamente, adicione a seguinte funcao ao codigo de cliente.py e modifique o programa principal para:@asyncio.coroutinedef roda_varias(vezes): pendente = [] for t in range(vezes): pendente.append(asyncio.async(loop.create_connection(EchoClient, '127.0.0.1', 8888))) for y in pendente: transporte, protocolo = yield from y yield from