trio.socket
The trio.socket
module provides trio's basic networking API.
Generally, trio.socket
's API mirrors that of the standard library socket
module. Most constants (like SOL_SOCKET
) and simple utilities (like ~socket.inet_aton
) are simply re-exported unchanged. But there are also some differences:
All functions that return sockets (e.g. socket.socket
, socket.socketpair
, ...) are modified to return trio sockets instead. In addition, there is a new function to directly convert a standard library socket into a trio socket:
from_stdlib_socket
The following functions have identical interfaces to their standard library version, but are now async
functions, so you need to use await
to call them:
~socket.getaddrinfo
~socket.getnameinfo
~socket.getfqdn
Trio intentionally DOES NOT include some obsolete, redundant, or broken features:
~socket.gethostbyname
,~socket.gethostbyname_ex
,~socket.gethostbyaddr
: obsolete; use~socket.getaddrinfo
and~socket.getnameinfo
instead.~socket.getdefaulttimeout
,~socket.setdefaulttimeout
: Use trio's standard support forcancellation
.- On Windows,
SO_REUSEADDR
is not exported, because it's a trap: the name is the same as UnixSO_REUSEADDR
, but the semantics are different and extremely broken. In the very rare cases where you actually wantSO_REUSEADDR
on Windows, then it can still be accessed from the standard library'ssocket
module.
Trio socket objects are overall very similar to the standard
library socket objects <python:socket-objects>
, with a few important differences:
Async all the things: Most obviously, everything is made "trio-style": blocking methods become async methods, and the following attributes are not supported:
~socket.socket.setblocking
: trio sockets always act like blocking sockets; if you need to read/write from multiple sockets at once, then create multiple tasks.~socket.socket.settimeout
: seecancellation
instead.~socket.socket.makefile
: Python's file-like API is synchronous, so it can't be implemented on top of an async socket.
No implicit name resolution: In the standard library socket
API, there are number of methods that take network addresses as arguments. When given a numeric address this is fine:
# OK
sock.bind(("127.0.0.1", 80))
sock.connect(("2607:f8b0:4000:80f::200e", 80))
But in the standard library, these methods also accept hostnames, and in this case implicitly trigger a DNS lookup to find the IP address:
# Might block!
sock.bind(("localhost", 80))
sock.connect(("google.com", 80))
This is problematic because DNS lookups are a blocking operation.
For simplicity, trio forbids such usages: hostnames must be "pre-resolved" to numeric addresses before they are passed to socket methods like bind
or connect
. In most cases this can be easily accomplished by calling either resolve_local_address
or resolve_remote_address
.
resolve_local_address
resolve_remote_address
Modern defaults: And finally, we took the opportunity to update the defaults for several socket options that were stuck in the 1980s. You can always use ~socket.socket.setsockopt
to change these back, but for trio sockets:
Everywhere except Windows,
SO_REUSEADDR
is enabled by default. This is almost always what you want, but if you're in one of the rare cases where this is undesireable then you can always disableSO_REUSEADDR
manually:sock.setsockopt(trio.socket.SOL_SOCKET, trio.socket.SO_REUSEADDR, False)
On Windows,
SO_EXCLUSIVEADDR
is enabled by default. Unfortunately, this means that if you stop and restart a server you may have trouble reacquiring listen ports (i.e., it acts like Unix withoutSO_REUSEADDR
). To get the Unix-styleSO_REUSEADDR
semantics on Windows, you can disableSO_EXCLUSIVEADDR
:sock.setsockopt(trio.socket.SOL_SOCKET, trio.socket.SO_EXCLUSIVEADDR, False)
but be warned that this may leave your application vulnerable to port hijacking attacks.
TCP_NODELAY
is enabled by default.IPV6_V6ONLY
is disabled, i.e., by default on dual-stack hosts aAF_INET6
socket is able to communicate with both IPv4 and IPv6 peers, where the IPv4 peers appear to be in the "IPv4-mapped" portion of IPv6 address space. To make an IPv6-only socket, use something like:sock = trio.socket.socket(trio.socket.AF_INET6) sock.setsockopt(trio.socket.IPPROTO_IPV6, trio.socket.IPV6_V6ONLY, True)
This makes trio applications behave more consistently across different environments.
- On platforms where it's supported (recent Linux and recent MacOS),
TCP_NOTSENT_LOWAT
is enabled with a reasonable buffer size (currently 16 KiB).
See issue #72 for discussion of these defaults.
The following methods are similar, but not identical, to the equivalents in socket.socket
:
bind
connect
sendall
sendfile
The following methods are not provided:
~socket.socket.send
: This method has confusing semantics hidden under a friendly name, and makes it too easy to create subtle bugs. Usesendall
instead.
The following methods are identical to their equivalents in socket.socket
, except async, and the ones that take address arguments require pre-resolved addresses:
~socket.socket.accept
~socket.socket.recv
~socket.socket.recv_into
~socket.socket.recvfrom
~socket.socket.recvfrom_into
~socket.socket.recvmsg
(if available)~socket.socket.recvmsg_into
(if available)~socket.socket.sendto
~socket.socket.sendmsg
(if available)
All methods and attributes not mentioned above are identical to their equivalents in socket.socket
:
~socket.socket.family
~socket.socket.type
~socket.socket.proto
~socket.socket.fileno
~socket.socket.listen
~socket.socket.getpeername
~socket.socket.getsockname
~socket.socket.close
~socket.socket.shutdown
~socket.socket.setsockopt
~socket.socket.getsockopt
~socket.socket.dup
~socket.socket.detach
~socket.socket.share
~socket.socket.set_inheritable
~socket.socket.get_inheritable
(this is currently more of a sketch than something actually useful, see issue #73)
trio
AsyncResource
SendStream
RecvStream
Stream
trio
catch_signals