Skip to content

Latest commit

 

History

History
256 lines (183 loc) · 8.36 KB

reference-io.rst

File metadata and controls

256 lines (183 loc) · 8.36 KB

I/O in Trio

Sockets and networking

trio.socket

The trio.socket module provides trio's basic networking API.

trio.socket's top-level exports

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 for cancellation.
  • On Windows, SO_REUSEADDR is not exported, because it's a trap: the name is the same as Unix SO_REUSEADDR, but the semantics are different and extremely broken. In the very rare cases where you actually want SO_REUSEADDR on Windows, then it can still be accessed from the standard library's socket module.

Socket objects

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: see cancellation 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:

  1. 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 disable SO_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 without SO_REUSEADDR). To get the Unix-style SO_REUSEADDR semantics on Windows, you can disable SO_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.

  2. TCP_NODELAY is enabled by default.
  3. IPV6_V6ONLY is disabled, i.e., by default on dual-stack hosts a AF_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.

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

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. Use sendall 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

The abstract Stream API

(this is currently more of a sketch than something actually useful, see issue #73)

trio

AsyncResource

SendStream

RecvStream

Stream

TLS support

Not implemented yet!

Async disk I/O

Not implemented yet!

Subprocesses

Not implemented yet!

Signals

trio

catch_signals