socket — modulo socket

Questo modulo fornisce l’accesso all’interfaccia socket BSD.

Differenze rispetto a CPython

Per efficienza e coerenza, gli oggetti socket in MicroPython implementano direttamente un’interfaccia stream (file-like). In CPython, è necessario convertire un socket in un oggetto file-like usando il metodo makefile(). Questo metodo è ancora supportato da MicroPython (ma è un’operazione nulla), quindi quando la compatibilità con CPython è importante, assicurati di usarlo.

Formato/i degli indirizzi socket

Il formato nativo degli indirizzi socket del modulo socket è un tipo di dato opaco restituito dalla funzione getaddrinfo(), che deve essere usata per risolvere un indirizzo testuale (inclusi gli indirizzi numerici):

sockaddr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# You must use getaddrinfo() even for numeric addresses
sockaddr = socket.getaddrinfo('127.0.0.1', 80)[0][-1]
# Now you can use that address
sock.connect(sockaddr)

L’uso di getaddrinfo() è il modo più efficiente (sia in termini di memoria che di potenza di elaborazione) e portabile per lavorare con gli indirizzi.

Il modulo socket fornisce anche un modo compatibile con CPython per specificare gli indirizzi usando tuple, come descritto di seguito. Sulla OpenMV Cam il modulo socket è integrato; gli indirizzi numerici possono essere forniti direttamente nel formato a tupla, ma i nomi di dominio devono prima essere risolti con getaddrinfo().

Riassumendo:

  • Usa sempre getaddrinfo() per risolvere i nomi host.

  • Gli indirizzi a tupla descritti di seguito possono essere usati come scorciatoia per gli indirizzi numerici, per prove rapide e l’uso interattivo.

Formato degli indirizzi a tupla per il modulo socket:

  • IPv4: (ipv4_address, port), dove ipv4_address è una stringa con un indirizzo IPv4 numerico in notazione puntata, ad esempio "8.8.8.8", e port è un numero di porta intero nell’intervallo 1-65535. I nomi di dominio non sono accettati come ipv4_address; risolvili prima usando getaddrinfo().

  • IPv6: (ipv6_address, port, flowinfo, scopeid), dove ipv6_address è una stringa con un indirizzo IPv6 numerico in notazione con due punti, ad esempio "2001:db8::1", e port è un numero di porta intero nell’intervallo 1-65535. flowinfo deve essere 0. scopeid è l’identificatore dello scope dell’interfaccia per gli indirizzi link-local. I nomi di dominio non sono accettati come ipv6_address; risolvili prima usando getaddrinfo().

Funzioni

socket.getaddrinfo(host: str, port: int, af: int = 0, type: int = 0, proto: int = 0, flags: int = 0, /) List[Tuple]

Traduce l’argomento host/port in una sequenza di tuple di 5 elementi che contengono tutti gli argomenti necessari per creare un socket connesso a quel servizio. Gli argomenti af, type e proto (che hanno lo stesso significato che hanno per la funzione socket) possono essere usati per filtrare quale tipo di indirizzi viene restituito. Se un parametro non è specificato o è zero, possono essere restituite tutte le combinazioni di indirizzi (richiedendo un filtraggio lato utente).

L’elenco risultante di tuple di 5 elementi ha la seguente struttura:

(family, type, proto, canonname, sockaddr)

L’esempio seguente mostra come connettersi a un dato url:

s = socket.socket()
# This assumes that if "type" is not specified, an address for
# SOCK_STREAM will be returned, which may be not true
s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1])

Uso consigliato dei parametri di filtraggio:

s = socket.socket()
# Guaranteed to return an address which can be connect'ed to for
# stream operation.
s.connect(socket.getaddrinfo('www.micropython.org', 80, 0, SOCK_STREAM)[0][-1])

Differenze rispetto a CPython

CPython solleva un’eccezione socket.gaierror (sottoclasse di OSError) in caso di errore in questa funzione. MicroPython non ha socket.gaierror e solleva direttamente OSError. Si noti che i numeri di errore di getaddrinfo() formano un namespace separato e potrebbero non corrispondere ai numeri di errore del modulo errno. Per distinguere gli errori di getaddrinfo(), essi sono rappresentati da numeri negativi, mentre gli errori di sistema standard sono numeri positivi (i numeri di errore sono accessibili usando la proprietà e.args[0] da un oggetto eccezione). L’uso di valori negativi è un dettaglio provvisorio che potrebbe cambiare in futuro.

socket.inet_ntop(af: int, bin_addr: bytes) str

Converte un indirizzo di rete binario bin_addr della famiglia di indirizzi af indicata in una rappresentazione testuale:

>>> socket.inet_ntop(socket.AF_INET, b"\x7f\0\0\1")
'127.0.0.1'
socket.inet_pton(af: int, txt_addr: str) bytes

Converte un indirizzo di rete testuale txt_addr della famiglia di indirizzi af indicata in una rappresentazione binaria:

>>> socket.inet_pton(socket.AF_INET, "1.2.3.4")
b'\x01\x02\x03\x04'

Costanti

socket.AF_INET: int

Famiglia di indirizzi IPv4.

socket.AF_INET6: int

Famiglia di indirizzi IPv6.

socket.SOCK_STREAM: int

Tipo di socket stream (TCP).

socket.SOCK_DGRAM: int

Tipo di socket datagram (UDP).

socket.SOCK_RAW: int

Tipo di socket raw.

socket.IPPROTO_IP: int

Il livello del protocollo IP. Usato come argomento level di setsockopt() insieme alle opzioni IP_*.

socket.IPPROTO_TCP: int

Il protocollo TCP. Non è necessario passarlo a socket (il tipo di socket SOCK_STREAM lo seleziona automaticamente); il suo unico utilizzo reale è come argomento level di setsockopt() insieme alle opzioni TCP_*.

socket.SOL_SOCKET: int

Il livello delle opzioni del socket. Usato come argomento level di setsockopt() insieme alle opzioni SO_*.

socket.SO_REUSEADDR: int

Consente al socket di effettuare il bind a un indirizzo/porta che si trova ancora nello stato TIME_WAIT.

socket.SO_BROADCAST: int

Consente l’invio di datagram a un indirizzo broadcast.

socket.SO_KEEPALIVE: int

Abilita la trasmissione periodica di sonde keep-alive su un socket connesso.

socket.SO_SNDTIMEO: int

Timeout di invio, in millisecondi, passato come argomento value a setsockopt().

socket.SO_RCVTIMEO: int

Timeout di ricezione, in millisecondi, passato come argomento value a setsockopt().

socket.IP_ADD_MEMBERSHIP: int

Si unisce a un gruppo multicast. Un’opzione di setsockopt() a livello IPPROTO_IP.

socket.IP_DROP_MEMBERSHIP: int

Abbandona un gruppo multicast. Un’opzione di setsockopt() a livello IPPROTO_IP.

socket.TCP_NODELAY: int

Disabilita l’algoritmo di Nagle. Un’opzione di setsockopt() a livello IPPROTO_TCP.

socket.MSG_PEEK: int

Per recv() / recvfrom(): restituisce i dati senza rimuoverli dalla coda di input.

socket.MSG_DONTWAIT: int

Per recv() / recvfrom(): esegue l’operazione in modalità non bloccante.

Classi

class socket.socket(af: int = AF_INET, type: int = SOCK_STREAM, proto: int = IPPROTO_TCP, /)

Crea un nuovo socket usando la famiglia di indirizzi, il tipo di socket e il numero di protocollo indicati. Specificare proto nella maggior parte dei casi non è necessario (e non è consigliato); l’argomento type seleziona automaticamente il protocollo necessario:

# Create STREAM TCP socket
socket(AF_INET, SOCK_STREAM)
# Create DGRAM UDP socket
socket(AF_INET, SOCK_DGRAM)
close() None

Contrassegna il socket come chiuso e rilascia tutte le risorse. Una volta che ciò avviene, tutte le operazioni future sull’oggetto socket falliranno. L’estremità remota riceverà un’indicazione EOF se supportata dal protocollo.

I socket vengono chiusi automaticamente quando vengono raccolti dal garbage collector, ma è consigliabile chiuderli esplicitamente con close() non appena hai finito di lavorare con essi.

bind(address: Any) None

Effettua il bind del socket a address. Il socket non deve essere già sottoposto a bind.

listen(backlog: int = 2) None

Consente a un server di accettare connessioni. Se backlog è specificato, deve essere almeno 0 (se è inferiore, verrà impostato a 0); e specifica il numero di connessioni non accettate che il sistema consentirà prima di rifiutare nuove connessioni. Se non specificato, viene scelto un valore predefinito ragionevole.

accept() Tuple['socket', Tuple]

Accetta una connessione. Il socket deve essere sottoposto a bind a un indirizzo e in ascolto di connessioni. Il valore di ritorno è una coppia (conn, address) dove conn è un nuovo oggetto socket utilizzabile per inviare e ricevere dati sulla connessione, e address è l’indirizzo associato al socket all’altra estremità della connessione.

connect(address: Any) None

Si connette a un socket remoto all’indirizzo address.

send(bytes: bytes) int

Invia dati al socket. Il socket deve essere connesso a un socket remoto. Restituisce il numero di byte inviati, che può essere inferiore alla lunghezza dei dati («short write»).

sendall(bytes: bytes) None

Invia tutti i dati al socket. Il socket deve essere connesso a un socket remoto. A differenza di send(), questo metodo tenterà di inviare tutti i dati, inviandoli un blocco alla volta in modo consecutivo.

Il comportamento di questo metodo sui socket non bloccanti non è definito. Per questo motivo, in MicroPython, è consigliabile usare invece il metodo write(), che ha la stessa politica di «nessuna short write» per i socket bloccanti e restituirà il numero di byte inviati sui socket non bloccanti.

recv(bufsize: int, flags: int = 0) bytes

Riceve dati dal socket. Il valore di ritorno è un oggetto bytes che rappresenta i dati ricevuti. La quantità massima di dati da ricevere in una sola volta è specificata da bufsize.

L’argomento opzionale flags è un OR bit a bit di flag di messaggio (MSG_PEEK, MSG_DONTWAIT), che hanno lo stesso significato che hanno in CPython.

sendto(bytes: bytes, address: Any) int

Invia dati al socket. Il socket non deve essere connesso a un socket remoto, poiché il socket di destinazione è specificato da address.

recvfrom(bufsize: int, flags: int = 0) Tuple[bytes, Tuple]

Riceve dati dal socket. Il valore di ritorno è una coppia (bytes, address) dove bytes è un oggetto bytes che rappresenta i dati ricevuti e address è l’indirizzo del socket che invia i dati.

Vedi la funzione recv() per una spiegazione dell’argomento opzionale flags.

setsockopt(level: int, optname: int, value: int | bytes) None

Imposta il valore dell’opzione del socket indicata. Le costanti simboliche necessarie sono definite nel modulo socket (SO_* ecc.). Il value può essere un intero o un oggetto bytes-like che rappresenta un buffer.

settimeout(value: float | None) None

Imposta un timeout sulle operazioni del socket bloccanti. L’argomento value può essere un numero in virgola mobile non negativo che esprime secondi, oppure None. Se viene fornito un valore diverso da zero, le successive operazioni sul socket solleveranno un’eccezione OSError se il periodo di timeout è trascorso prima che l’operazione sia completata. Se viene fornito zero, il socket viene messo in modalità non bloccante. Se viene fornito None, il socket viene messo in modalità bloccante.

Un’alternativa portabile e generica è usare un oggetto select.poll. Questo consente di attendere su più oggetti contemporaneamente (e non solo su socket, ma su generici oggetti stream che supportano il polling). Esempio:

# Instead of:
s.settimeout(1.0)  # time in seconds
s.read(10)  # may timeout

# Use:
poller = select.poll()
poller.register(s, select.POLLIN)
res = poller.poll(1000)  # time in milliseconds
if not res:
    # s is still not ready for input, i.e. operation timed out

Differenze rispetto a CPython

CPython solleva un’eccezione socket.timeout in caso di timeout, che è una sottoclasse di OSError. MicroPython solleva invece direttamente un OSError. Se usi except OSError: per catturare l’eccezione, il tuo codice funzionerà sia in MicroPython che in CPython.

setblocking(flag: bool) None

Imposta la modalità bloccante o non bloccante del socket: se flag è falso, il socket viene impostato come non bloccante, altrimenti come bloccante.

Questo metodo è una scorciatoia per determinate chiamate a settimeout():

  • sock.setblocking(True) equivale a sock.settimeout(None)

  • sock.setblocking(False) equivale a sock.settimeout(0)

makefile(mode: str = 'rb', buffering: int = 0, /) Any

Restituisce un oggetto file associato al socket. Il tipo esatto restituito dipende dagli argomenti forniti a makefile(). Il supporto è limitato alle sole modalità binarie (“rb”, “wb” e “rwb”). Gli argomenti di CPython encoding, errors e newline non sono supportati.

Differenze rispetto a CPython

Poiché MicroPython non supporta gli stream con buffer, il valore del parametro buffering viene ignorato e trattato come se fosse 0 (senza buffer).

Differenze rispetto a CPython

La chiusura dell’oggetto file restituito da makefile() chiuderà ANCHE il socket originale.

read(size: int | None = None) bytes

Legge fino a size byte dal socket. Restituisce un oggetto bytes. Se size non è fornito, legge tutti i dati disponibili dal socket fino all’EOF; pertanto il metodo non ritornerà finché il socket non viene chiuso. Questa funzione tenta di leggere tutti i dati richiesti (nessuna «short read»). Tuttavia ciò potrebbe non essere possibile con un socket non bloccante, e in tal caso verranno restituiti meno dati.

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

Legge byte in buf. Se nbytes è specificato, legge al massimo quel numero di byte. Altrimenti, legge al massimo len(buf) byte. Proprio come read(), questo metodo segue la politica di «nessuna short read».

Valore di ritorno: numero di byte letti e memorizzati in buf.

readline() bytes

Legge una riga, terminata da un carattere di nuova riga.

Valore di ritorno: la riga letta.

write(buf: bytes) int

Scrive il buffer di byte nel socket. Questa funzione tenterà di scrivere tutti i dati su un socket (nessuna «short write»). Tuttavia ciò potrebbe non essere possibile con un socket non bloccante, e il valore restituito sarà inferiore alla lunghezza di buf.

Valore di ritorno: numero di byte scritti.

Nota

MicroPython non implementa socket.error. CPython ha un’eccezione deprecata socket.error che è un alias di OSError; in MicroPython, usa direttamente OSError per catturare gli errori relativi ai socket.