Programação Assíncrona em Python: Trabalhando com Fluxos Não-Bloqueantes

Uma das prnicipais vantagens do asyncio é a capacidade de utilizar fluxos não-bloqueantes para operações de I/O.

  1. 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.

  1. 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)

  1. 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)

  1. 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()

  1. 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

Tags: Python asyncio programacao-assincrona fluxos-nao-bloqueantes sockets

Publicado em 5-30 23:57 por Thomas