9.11. Objetos socket

A interface Python para a camada de transporte é a classe socket.socket. Um socket representa um extremo de uma conversa de rede – um endereço, uma porta e o protocolo (UDP ou TCP) sobre o qual a conversa decorre. Os capítulos de controlo de hardware abriram instâncias de UART para comunicar num fio; esta secção abre instâncias de socket para comunicar na rede. A forma é a mesma; o serviço subjacente é simplesmente muito mais capaz.

9.11.1. Criar um socket

Três argumentos descrevem um socket: qual a família de endereços que usa, qual o tipo de socket que oferece, e qual o protocolo que utiliza. Os valores predefinidos cobrem os casos que o resto desta secção usa:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # IPv4 TCP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    # IPv4 UDP

As duas constantes entre as quais a aplicação escolhe em quase todos os casos:

  • AF_INET – endereços IPv4 (um IP numérico mais uma porta). A escolha mais comum. AF_INET6 é o equivalente para IPv6.

  • SOCK_STREAM – uma ligação TCP, o fluxo de bytes fiável. SOCK_DGRAM é o equivalente para UDP.

O terceiro argumento (o protocolo) é deixado em zero, o que escolhe o valor predefinido correto com base nos primeiros dois. O construtor completo está documentado em socket — módulo socket.

9.11.2. Endereços num socket

Um endereço de socket é um tuplo de (host, port)

("192.168.1.50", 80)
("0.0.0.0", 8000)

O host é o endereço IP como uma string. A porta é o inteiro de 16 bits abordado em Portas.

Vale a pena conhecer um conjunto de strings de host especiais:

  • "0.0.0.0" significa «todas as interfaces IPv4 neste dispositivo». Um servidor ligado a este endereço aceita ligações em qualquer endereço que a câmara possua.

  • "127.0.0.1" é o localhost – o tráfego para ele nunca sai do dispositivo. Útil para testes.

  • "255.255.255.255" é o endereço de broadcast local. Um datagrama UDP enviado para ele vai para todos os dispositivos no segmento local.

Nomes de domínio como "example.com" não são strings de host válidas num endereço de socket. Têm de ser resolvidos para um IP primeiro; Nomes e DNS aborda a chamada getaddrinfo() que faz isso.

9.11.3. Os dois papéis

O ciclo de vida de um socket depende de que lado da conversa ele está. Um socket cliente chama connect() (ou, para UDP, simplesmente sendto()) para comunicar com um servidor conhecido. Um socket servidor chama bind() para reclamar uma porta, depois listen() e accept() (para TCP) ou recvfrom() (para UDP) para receber tráfego de entrada.

O mesmo construtor socket é utilizado em ambos os casos; apenas os métodos chamados a seguir diferem. As três páginas seguintes percorrem os padrões práticos:

9.11.4. Fechar um socket

Cada socket mantém uma pequena parte do estado do sistema operativo (uma reserva de porta, buffers, o estado TCP da ligação). Quando a aplicação termina com ele, close() liberta esse estado. Um socket esquecido é uma fuga lenta que se vai acumulando; num ciclo que abre ligações, não invocar close acabará por esgotar o conjunto de sockets disponíveis na câmara.

O padrão mais correto é a instrução with

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(addr)
    s.send(b"GET / HTTP/1.0\r\n\r\n")
    ...
# socket is closed automatically here, even on error

Os sockets implementam o protocolo de gestor de contexto abordado na visão geral de Python, pelo que o bloco with garante que close() é chamado independentemente de o bloco ter terminado normalmente ou por levantamento de uma exceção.

9.11.5. A referência socket

Esta e as páginas seguintes percorrem a API de forma narrativa. Para a referência completa ao nível dos argumentos de cada método, cada sinalizador e cada constante que o módulo expõe, consulte socket — módulo socket. A referência é também o local para procurar as operações menos comuns (opções de socket, adesão a grupos multicast, IDs de âmbito IPv6) que esta secção não cobre.