Uma das prnicipais vantagens do asyncio é a capacidade de utilizar fluxos não-bloqueantes para operações de I/O.
- Compreendendo Fluxos Assíncronos
O framework asyncio oferece suporte para programação de soquetes I/O não-blocantes através de fluxos (streams). Esses fluxos permitem abrir conexões que fornecem acesso a leitores e escritores, permitindo que as corrotinas leiam e escrevam dados pausando quando necessário.
É importante notar que os recursos de fluxo assíncrono são de baixo nível, exigindo implementação manual de protocolos específicos. Isso inclui protocolos web comuns como:
- HTTP/HTTPS para comunicação com servidores web
- SMTP para interação com servidores de email
- FTP para comunicação com servidores de aruqivos
Esses fluxos também podem ser utilizados para criar servidores que processam requisições usando protocolos padrão ou para desenvolver prootcolos específicos de aplicação.
- Estabelecendo Conexões de Cliente
Para abrir conexões de cliente TCP com asyncio, utiliza-se a função asyncio.open_connection(). Esta é uma corrotina que deve ser aguardada e retorna objetos StreamReader e StreamWriter após a conexão ser estabelecida:
# Estabelecendo uma conexão
leitor, escritor = await asyncio.open_connection(...)
A função aceita diversos parâmetros para configurar a conexão, sendo obrigatórios o host e a porta. O host pode ser um nome de domínio ou endereço IP, enquanto a porta especifica o número do soquete:
# Conectando-se a um servidor HTTP
leitor, escritor = await asyncio.open_connection('www.example.com', 80)
Para conexões criptografadas via SSL (como HTTPS), basta definir o parâmetro ssl como True:
# Conectando-se a um servidor HTTPS
leitor, escritor = await asyncio.open_connection('www.example.com', 443, ssl=True)
- Criando Servidores
Para implementar um servidor TCP com asyncio, utiliza-se a função asyncio.start_server(), que também é uma corrotina. Ela retorna um objeto asyncio.Server representando o servidor em execução:
# Iniciando um servidor TCP
servidor = await asyncio.start_server(...)
Os parâmetros obrigatórios são uma função de callback, o host e a porta. A função de callback é executada sempre que um cliente se conecta ao servidor:
# Função para tratar conexões
async def manipulador(leitor, escritor):
# Lógica de manipulação da conexão
pass
# Iniciando um servidor para receber conexões HTTP
servidor = await asyncio.start_server(manipulador, '127.0.0.1', 80)
- Escrevendo Dados com StreamWriter
O objeto StreamWriter permite enviar dados através do soquete. Os dados devem ser fornecidos em formato de bytes:
# Escrevendo dados em bytes
escritor.write(dados_bytes)
Também é possível escrever múltiplas "linhas" de dados organizadas em uma lista ou iterável usando o método writelines():
# Escrevendo múltiplas linhas de bytes
escritor.writelines(lista_linhas_bytes)
Após escrever dados, é recomendável chamar o método drain() para garantir que todos os bytes sejam transmitidos antes de continuar:
# Escrevendo dados em bytes
escritor.write(dados_bytes)
# Aguardando transmissão dos dados
await escritor.drain()
- Lendo Dados com StreamReader
O objeto StreamReader permite receber dados do soquete, também em formato de bytes. Todos os métodos de leitura são corrotinas que devem ser aguardadas:
O método read() lê uma quantidade arbitrária de bytes até o final do arquivo (EOF):
# Lendo dados em bytes
dados_bytes = await leitor.read()
Também é possível especificar um número exato de bytes a serem lidos através do parâmetro n:
# Lendo um número específico de bytes
dados_bytes = await leitor.read(n=100)
Para ler uma linha de texto até encontrar um caractere de nova linha (\n), utiliza-se o método readline():
# Lendo uma linha de dados
linha_bytes = await leitor.readline()
Existem também métodos como readexactly() que lê exatamente a quantidade especificada de bytes (lançando exceção caso não consiga) e readuntil() que lê até encontrar um caractere específico.
6> Encerrando Conexões
Para fechar um soquete, utiliza-se o método close() do objeto StreamWriter. Este método não bloqueia a execução:
# Fechando o soquete
escritor.close()
Embora close() não bloqueie, podemos aguardar o soquete ser completamente fechado com o método wait_closed():
# Fechando o soquete
escritor.close()
# Aguardando o soquete ser fechado
await escritor.wait_closed()
Para verificar se um soquete já foi fechado ou está em processo de fechamento, utiliza-se o método is_closing():
# Verificando se o soquete está fechando
if escritor.is_closing():
# Ações específicas
pass