抽象流 API

The abstract Stream API

Trio 提供了一组抽象基类, 用于定义单向和双向字节流的标准接口。

为什么这很有用?因为它允许你编写通用的协议实现, 这些实现可以在任意传输层上工作, 并且能够轻松创建复杂的传输配置。以下是一些示例:

  • trio.SocketStream 封装一个原始套接字 (比如通过网络的 TCP 连接 ) , 并将其转换为标准的流接口。

  • trio.SSLStream 是一个“流适配器”, 可以将任何实现了 trio.abc.Stream 接口的对象转换为加密流。在 Trio 中, 通过在 SocketStream 周围包装一个 SSLStream, 是实现网络 SSL 通信的标准方式。

  • 如果你启动一个 子进程, 你可以获得一个 SendStream, 它允许你写入子进程的 stdin, 以及一个 ReceiveStream, 它允许你从子进程的 stdout 中读取。如果你出于某种原因想要通过 SSL 与子进程通信, 可以使用 StapledStream 将子进程的 stdin/stdout 合并成一个双向的 Stream, 然后将其包装在一个 SSLStream 中:

    ssl_context = ssl.create_default_context()
    ssl_context.check_hostname = False
    s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context)
    
  • 有时你需要连接到一个 HTTPS 服务器, 但必须通过一个 Web 代理, 而代理本身也使用 HTTPS。这时, 你就需要进行 SSL-on-top-of-SSL。在 Trio 中, 这非常简单——只需将第一个 SSLStream 再次包装在第二个 SSLStream 中:

    # 获取到代理的原始 SocketStream 连接:
    s0 = await open_tcp_stream("proxy", 443)
    
    # 设置与代理的 SSL 连接:
    s1 = SSLStream(s0, proxy_ssl_context, server_hostname="proxy")
    # 请求连接到网站
    await s1.send_all(b"CONNECT website:443 / HTTP/1.0\r\n\r\n")
    await check_CONNECT_response(s1)
    
    # 设置与真实网站的 SSL 连接。注意, s1 已经是一个 SSLStream 对象,
    # 这里我们将第二个 SSLStream 对象包装在它周围。
    s2 = SSLStream(s1, website_ssl_context, server_hostname="website")
    # 发出请求
    await s2.send_all(b"GET /index.html HTTP/1.0\r\n\r\n")
    ...
    
  • trio.testing 模块提供了一组 灵活的内存流对象实现, 因此如果你有一个协议实现需要测试, 你可以启动两个任务, 设置一个虚拟的“套接字”连接它们, 然后进行诸如在连接中注入随机但可重复的延迟之类的操作。

Trio provides a set of abstract base classes that define a standard interface for unidirectional and bidirectional byte streams.

Why is this useful? Because it lets you write generic protocol implementations that can work over arbitrary transports, and easily create complex transport configurations. Here's some examples:

  • trio.SocketStream wraps a raw socket (like a TCP connection over the network), and converts it to the standard stream interface.

  • trio.SSLStream is a "stream adapter" that can take any object that implements the trio.abc.Stream interface, and convert it into an encrypted stream. In Trio the standard way to speak SSL over the network is to wrap an SSLStream around a SocketStream.

  • If you spawn a subprocess, you can get a SendStream that lets you write to its stdin, and a ReceiveStream that lets you read from its stdout. If for some reason you wanted to speak SSL to a subprocess, you could use a StapledStream to combine its stdin/stdout into a single bidirectional Stream, and then wrap that in an SSLStream:

    ssl_context = ssl.create_default_context()
    ssl_context.check_hostname = False
    s = SSLStream(StapledStream(process.stdin, process.stdout), ssl_context)
    
  • It sometimes happens that you want to connect to an HTTPS server, but you have to go through a web proxy... and the proxy also uses HTTPS. So you end up having to do SSL-on-top-of-SSL. In Trio this is trivial – just wrap your first SSLStream in a second SSLStream:

    # Get a raw SocketStream connection to the proxy:
    s0 = await open_tcp_stream("proxy", 443)
    
    # Set up SSL connection to proxy:
    s1 = SSLStream(s0, proxy_ssl_context, server_hostname="proxy")
    # Request a connection to the website
    await s1.send_all(b"CONNECT website:443 / HTTP/1.0\r\n\r\n")
    await check_CONNECT_response(s1)
    
    # Set up SSL connection to the real website. Notice that s1 is
    # already an SSLStream object, and here we're wrapping a second
    # SSLStream object around it.
    s2 = SSLStream(s1, website_ssl_context, server_hostname="website")
    # Make our request
    await s2.send_all(b"GET /index.html HTTP/1.0\r\n\r\n")
    ...
    
  • The trio.testing module provides a set of flexible in-memory stream object implementations, so if you have a protocol implementation to test then you can start two tasks, set up a virtual "socket" connecting them, and then do things like inject random-but-repeatable delays into the connection.

抽象基类

Abstract base classes

class trio.abc.AsyncResource

基类:ABC

A standard interface for resources that needs to be cleaned up, and where that cleanup may require blocking operations.

This class distinguishes between "graceful" closes, which may perform I/O and thus block, and a "forceful" close, which cannot. For example, cleanly shutting down a TLS-encrypted connection requires sending a "goodbye" message; but if a peer has become non-responsive, then sending this message might block forever, so we may want to just drop the connection instead. Therefore the aclose() method is unusual in that it should always close the connection (or at least make its best attempt) even if it fails; failure indicates a failure to achieve grace, not a failure to close the connection.

Objects that implement this interface can be used as async context managers, i.e., you can write:

async with create_resource() as some_async_resource:
    ...

Entering the context manager is synchronous (not a checkpoint); exiting it calls aclose(). The default implementations of __aenter__ and __aexit__ should be adequate for all subclasses.

abstractmethod await aclose()

Close this resource, possibly blocking.

IMPORTANT: This method may block in order to perform a "graceful" shutdown. But, if this fails, then it still must close any underlying resources before returning. An error from this method indicates a failure to achieve grace, not a failure to close the connection.

For example, suppose we call aclose() on a TLS-encrypted connection. This requires sending a "goodbye" message; but if the peer has become non-responsive, then our attempt to send this message might block forever, and eventually time out and be cancelled. In this case the aclose() method on SSLStream will immediately close the underlying transport stream using trio.aclose_forcefully() before raising Cancelled.

If the resource is already closed, then this method should silently succeed.

Once this method completes, any other pending or future operations on this resource should generally raise ClosedResourceError, unless there's a good reason to do otherwise.

See also: trio.aclose_forcefully().

返回类型:

None

await trio.aclose_forcefully(resource)

Close an async resource or async generator immediately, without blocking to do any graceful cleanup.

AsyncResource objects guarantee that if their aclose() method is cancelled, then they will still close the resource (albeit in a potentially ungraceful fashion). aclose_forcefully() is a convenience function that exploits this behavior to let you force a resource to be closed without blocking: it works by calling await resource.aclose() and then cancelling it immediately.

Most users won't need this, but it may be useful on cleanup paths where you can't afford to block, or if you want to close a resource and don't care about handling it gracefully. For example, if SSLStream encounters an error and cannot perform its own graceful close, then there's no point in waiting to gracefully shut down the underlying transport either, so it calls await aclose_forcefully(self.transport_stream).

Note that this function is async, and that it acts as a checkpoint, but unlike most async functions it cannot block indefinitely (at least, assuming the underlying resource object is correctly implemented).

返回类型:

None

class trio.abc.SendStream

基类:AsyncResource

A standard interface for sending data on a byte stream.

The underlying stream may be unidirectional, or bidirectional. If it's bidirectional, then you probably want to also implement ReceiveStream, which makes your object a Stream.

SendStream objects also implement the AsyncResource interface, so they can be closed by calling aclose() or using an async with block.

If you want to send Python objects rather than raw bytes, see SendChannel.

abstractmethod await send_all(data)

Sends the given data through the stream, blocking if necessary.

参数:

data (bytes, bytearray, or memoryview) -- The data to send.

抛出:

Most low-level operations in Trio provide a guarantee: if they raise trio.Cancelled, this means that they had no effect, so the system remains in a known state. This is not true for :rtype: None send_all(). If this operation raises trio.Cancelled (or any other exception for that matter), then it may have sent some, all, or none of the requested data, and there is no way to know which.

abstractmethod await wait_send_all_might_not_block()

Block until it's possible that send_all() might not block.

This method may return early: it's possible that after it returns, send_all() will still block. (In the worst case, if no better implementation is available, then it might always return immediately without blocking. It's nice to do better than that when possible, though.)

This method must not return late: if it's possible for send_all() to complete without blocking, then it must return. When implementing it, err on the side of returning early.

抛出:

备注

This method is intended to aid in implementing protocols that want to delay choosing which data to send until the last moment. E.g.,

返回类型:

None

suppose you're working on an implementation of a remote display server like VNC, and the network connection is currently backed up so that if you call send_all() now then it will sit for 0.5 seconds before actually sending anything. In this case it doesn't make sense to take a screenshot, then wait 0.5 seconds, and then send it, because the screen will keep changing while you wait; it's better to wait 0.5 seconds, then take the screenshot, and then send it, because this way the data you deliver will be more up-to-date. Using wait_send_all_might_not_block() makes it possible to implement the better strategy.

If you use this method, you might also want to read up on TCP_NOTSENT_LOWAT.

Further reading:

class trio.abc.ReceiveStream

基类:AsyncResource

A standard interface for receiving data on a byte stream.

The underlying stream may be unidirectional, or bidirectional. If it's bidirectional, then you probably want to also implement SendStream, which makes your object a Stream.

ReceiveStream objects also implement the AsyncResource interface, so they can be closed by calling aclose() or using an async with block.

If you want to receive Python objects rather than raw bytes, see ReceiveChannel.

ReceiveStream objects can be used in async for loops. Each iteration will produce an arbitrary sized chunk of bytes, like calling receive_some with no arguments. Every chunk will contain at least one byte, and the loop automatically exits when reaching end-of-file.

abstractmethod await receive_some(max_bytes=None)

Wait until there is data available on this stream, and then return some of it.

A return value of b"" (an empty bytestring) indicates that the stream has reached end-of-file. Implementations should be careful that they return b"" if, and only if, the stream has reached end-of-file!

参数:

max_bytes (int) -- The maximum number of bytes to return. Must be greater than zero. Optional; if omitted, then the stream object is free to pick a reasonable default.

返回:

The data received.

返回类型:

bytes or bytearray

抛出:
class trio.abc.Stream

基类:SendStream, ReceiveStream

A standard interface for interacting with bidirectional byte streams.

A Stream is an object that implements both the SendStream and ReceiveStream interfaces.

If implementing this interface, you should consider whether you can go one step further and implement HalfCloseableStream.

class trio.abc.HalfCloseableStream

基类:Stream

This interface extends Stream to also allow closing the send part of the stream without closing the receive part.

abstractmethod await send_eof()

Send an end-of-file indication on this stream, if possible.

The difference between send_eof() and aclose() is that send_eof() is a unidirectional end-of-file indication. After you call this method, you shouldn't try sending any more data on this stream, and your remote peer should receive an end-of-file indication (eventually, after receiving all the data you sent before that). But, they may continue to send data to you, and you can continue to receive it by calling receive_some(). You can think of it as calling aclose() on just the SendStream "half" of the stream object (and in fact that's literally how trio.StapledStream implements it).

Examples: :rtype: None

  • On a socket, this corresponds to shutdown(..., SHUT_WR) (man page).

  • The SSH protocol provides the ability to multiplex bidirectional "channels" on top of a single encrypted connection. A Trio implementation of SSH could expose these channels as HalfCloseableStream objects, and calling send_eof() would send an SSH_MSG_CHANNEL_EOF request (see RFC 4254 §5.3).

  • On an SSL/TLS-encrypted connection, the protocol doesn't provide any way to do a unidirectional shutdown without closing the connection entirely, so SSLStream implements Stream, not HalfCloseableStream.

If an EOF has already been sent, then this method should silently succeed.

抛出:
class trio.abc.Listener

基类:AsyncResource, Generic[T_resource]

A standard interface for listening for incoming connections.

Listener objects also implement the AsyncResource interface, so they can be closed by calling aclose() or using an async with block.

abstractmethod await accept()

Wait until an incoming connection arrives, and then return it.

返回:

An object representing the incoming connection. In practice this is generally some kind of Stream, but in principle you could also define a Listener that returned, say, channel objects.

返回类型:

AsyncResource

抛出:

Listeners don't generally raise BrokenResourceError, because for listeners there is no general condition of "the network/remote peer broke the connection" that can be handled in a generic way, like there is for streams. Other errors can occur and be raised from accept() – for example, if you run out of file descriptors then you might get an OSError with its errno set to EMFILE.

class trio.abc.SendChannel

基类:AsyncResource, Generic[SendType]

A standard interface for sending Python objects to some receiver.

SendChannel objects also implement the AsyncResource interface, so they can be closed by calling ~AsyncResource.aclose or using an async with block.

If you want to send raw bytes rather than Python objects, see SendStream.

abstractmethod await send(value)

Attempt to send an object through the channel, blocking if necessary.

参数:

value (object) -- The object to send.

抛出:
  • trio.BrokenResourceError -- if something has gone wrong, and the channel is broken. For example, you may get this if the receiver has already been closed.

  • trio.ClosedResourceError -- if you previously closed this SendChannel object, or if another task closes it while send() is running.

  • trio.BusyResourceError -- some channels allow multiple tasks to call send at the same time, but others don't. If you try to call send simultaneously from multiple tasks on a channel that doesn't support it, then you can get ~trio.BusyResourceError.

返回类型:

None

class trio.abc.ReceiveChannel

基类:AsyncResource, Generic[ReceiveType]

A standard interface for receiving Python objects from some sender.

You can iterate over a ReceiveChannel using an async for loop:

async for value in receive_channel:
    ...

This is equivalent to calling receive() repeatedly. The loop exits without error when receive raises ~trio.EndOfChannel.

ReceiveChannel objects also implement the AsyncResource interface, so they can be closed by calling ~AsyncResource.aclose or using an async with block.

If you want to receive raw bytes rather than Python objects, see ReceiveStream.

abstractmethod await receive()

Attempt to receive an incoming object, blocking if necessary.

返回:

Whatever object was received.

返回类型:

object

抛出:
  • trio.EndOfChannel -- if the sender has been closed cleanly, and no more objects are coming. This is not an error condition.

  • trio.ClosedResourceError -- if you previously closed this ReceiveChannel object.

  • trio.BrokenResourceError -- if something has gone wrong, and the channel is broken.

  • trio.BusyResourceError -- some channels allow multiple tasks to call receive at the same time, but others don't. If you try to call receive simultaneously from multiple tasks on a channel that doesn't support it, then you can get ~trio.BusyResourceError.

class trio.abc.Channel

基类:SendChannel[T], ReceiveChannel[T]

A standard interface for interacting with bidirectional channels.

A Channel is an object that implements both the SendChannel and ReceiveChannel interfaces, so you can both send and receive objects.

通用流工具

Generic stream tools

Trio 目前提供了一个通用的助手, 用于编写监听连接的服务器, 使用一个或多个 Listeners, 并且提供了一个通用的工具类, 用于处理流。如果你想测试针对流接口编写的代码, 应该查看 trio.testing 中的

await trio.serve_listeners(handler, listeners, *, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections on listeners, and for each one start a task running handler(stream).

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

参数:
返回类型:

NoReturn

返回:

This function never returns unless cancelled.

Resource handling:

If handler neglects to close the stream, then it will be closed using trio.aclose_forcefully().

Error handling:

Most errors coming from accept() are allowed to propagate out (crashing the server in the process). However, some errors – those which indicate that the server is temporarily overloaded – are handled specially. These are OSErrors with one of the following errnos:

  • EMFILE: process is out of file descriptors

  • ENFILE: system is out of file descriptors

  • ENOBUFS, ENOMEM: the kernel hit some sort of memory limitation when trying to create a socket object

When serve_listeners() gets one of these errors, then it:

  • Logs the error to the standard library logger trio.serve_listeners (level = ERROR, with exception information included). By default this causes it to be printed to stderr.

  • Waits 100 ms before calling accept again, in hopes that the system will recover.

class trio.StapledStream(send_stream, receive_stream)

基类:HalfCloseableStream, Generic[SendStreamT, ReceiveStreamT]

This class staples together two unidirectional streams to make single bidirectional stream.

参数:
  • send_stream (SendStream) -- The stream to use for sending.

  • receive_stream (ReceiveStream) -- The stream to use for receiving.

示例

A silly way to make a stream that echoes back whatever you write to it:

left, right = trio.testing.memory_stream_pair()
echo_stream = StapledStream(SocketStream(left), SocketStream(right))
await echo_stream.send_all(b"x")
assert await echo_stream.receive_some() == b"x"

StapledStream objects implement the methods in the HalfCloseableStream interface. They also have two additional public attributes:

send_stream

The underlying SendStream. send_all() and wait_send_all_might_not_block() are delegated to this object.

receive_stream

The underlying ReceiveStream. receive_some() is delegated to this object.

await aclose()

Calls aclose on both underlying streams.

返回类型:

None

await receive_some(max_bytes=None)

Calls self.receive_stream.receive_some.

返回类型:

bytes

await send_all(data)

Calls self.send_stream.send_all.

返回类型:

None

await send_eof()

Shuts down the send side of the stream.

If self.send_stream.send_eof() exists, then this calls it. Otherwise, this calls self.send_stream.aclose().

返回类型:

None

await wait_send_all_might_not_block()

Calls self.send_stream.wait_send_all_might_not_block.

返回类型:

None

Trio currently provides a generic helper for writing servers that listen for connections using one or more Listeners, and a generic utility class for working with streams. And if you want to test code that's written against the streams interface, you should also check out in trio.testing.

await trio.serve_listeners(handler, listeners, *, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections on listeners, and for each one start a task running handler(stream).

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

参数:
返回类型:

NoReturn

返回:

This function never returns unless cancelled.

Resource handling:

If handler neglects to close the stream, then it will be closed using trio.aclose_forcefully().

Error handling:

Most errors coming from accept() are allowed to propagate out (crashing the server in the process). However, some errors – those which indicate that the server is temporarily overloaded – are handled specially. These are OSErrors with one of the following errnos:

  • EMFILE: process is out of file descriptors

  • ENFILE: system is out of file descriptors

  • ENOBUFS, ENOMEM: the kernel hit some sort of memory limitation when trying to create a socket object

When serve_listeners() gets one of these errors, then it:

  • Logs the error to the standard library logger trio.serve_listeners (level = ERROR, with exception information included). By default this causes it to be printed to stderr.

  • Waits 100 ms before calling accept again, in hopes that the system will recover.

class trio.StapledStream(send_stream, receive_stream)

基类:HalfCloseableStream, Generic[SendStreamT, ReceiveStreamT]

This class staples together two unidirectional streams to make single bidirectional stream.

参数:
  • send_stream (SendStream) -- The stream to use for sending.

  • receive_stream (ReceiveStream) -- The stream to use for receiving.

示例

A silly way to make a stream that echoes back whatever you write to it:

left, right = trio.testing.memory_stream_pair()
echo_stream = StapledStream(SocketStream(left), SocketStream(right))
await echo_stream.send_all(b"x")
assert await echo_stream.receive_some() == b"x"

StapledStream objects implement the methods in the HalfCloseableStream interface. They also have two additional public attributes:

send_stream

The underlying SendStream. send_all() and wait_send_all_might_not_block() are delegated to this object.

receive_stream

The underlying ReceiveStream. receive_some() is delegated to this object.

await aclose()

Calls aclose on both underlying streams.

返回类型:

None

await receive_some(max_bytes=None)

Calls self.receive_stream.receive_some.

返回类型:

bytes

await send_all(data)

Calls self.send_stream.send_all.

返回类型:

None

await send_eof()

Shuts down the send side of the stream.

If self.send_stream.send_eof() exists, then this calls it. Otherwise, this calls self.send_stream.aclose().

返回类型:

None

await wait_send_all_might_not_block()

Calls self.send_stream.wait_send_all_might_not_block.

返回类型:

None

套接字和网络

Sockets and networking

高级网络接口是建立在我们的流抽象之上的。

await trio.open_tcp_stream(host, port, *, happy_eyeballs_delay=0.25, local_address=None)

Connect to the given host and port over TCP.

If the given host has multiple IP addresses associated with it, then we have a problem: which one do we use?

One approach would be to attempt to connect to the first one, and then if that fails, attempt to connect to the second one ... until we've tried all of them. But the problem with this is that if the first IP address is unreachable (for example, because it's an IPv6 address and our network discards IPv6 packets), then we might end up waiting tens of seconds for the first connection attempt to timeout before we try the second address.

Another approach would be to attempt to connect to all of the addresses at the same time, in parallel, and then use whichever connection succeeds first, abandoning the others. This would be fast, but create a lot of unnecessary load on the network and the remote server.

This function strikes a balance between these two extremes: it works its way through the available addresses one at a time, like the first approach; but, if happy_eyeballs_delay seconds have passed and it's still waiting for an attempt to succeed or fail, then it gets impatient and starts the next connection attempt in parallel. As soon as any one connection attempt succeeds, all the other attempts are cancelled. This avoids unnecessary load because most connections will succeed after just one or two attempts, but if one of the addresses is unreachable then it doesn't slow us down too much.

This is known as a "happy eyeballs" algorithm, and our particular variant is modelled after how Chrome connects to webservers; see RFC 6555 for more details.

参数:
  • host (str or bytes) -- The host to connect to. Can be an IPv4 address, IPv6 address, or a hostname.

  • port (int) -- The port to connect to.

  • happy_eyeballs_delay (float or None) -- How many seconds to wait for each connection attempt to succeed or fail before getting impatient and starting another one in parallel. Set to None if you want to limit to only one connection attempt at a time (like socket.create_connection()). Default: 0.25 (250 ms).

  • local_address (None or str) --

    The local IP address or hostname to use as the source for outgoing connections. If None, we let the OS pick the source IP.

    This is useful in some exotic networking configurations where your host has multiple IP addresses, and you want to force the use of a specific one.

    Note that if you pass an IPv4 local_address, then you won't be able to connect to IPv6 hosts, and vice-versa. If you want to take advantage of this to force the use of IPv4 or IPv6 without specifying an exact source address, you can use the IPv4 wildcard address local_address="0.0.0.0", or the IPv6 wildcard address local_address="::".

返回:

a Stream connected to the given server.

返回类型:

SocketStream

抛出:

OSError -- if the connection fails.

参见

open_ssl_over_tcp_stream

await trio.serve_tcp(handler, port, *, host=None, backlog=None, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming TCP connections, and for each one start a task running handler(stream).

This is a thin convenience wrapper around open_tcp_listeners() and serve_listeners() – see them for full details.

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

When used with nursery.start you get back the newly opened listeners. So, for example, if you want to start a server in your test suite and then connect to it to check that it's working properly, you can use something like:

from trio import SocketListener, SocketStream
from trio.testing import open_stream_to_socket_listener

async with trio.open_nursery() as nursery:
    listeners: list[SocketListener] = await nursery.start(serve_tcp, handler, 0)
    client_stream: SocketStream = await open_stream_to_socket_listener(listeners[0])

    # Then send and receive data on 'client_stream', for example:
    await client_stream.send_all(b"GET / HTTP/1.0\r\n\r\n")

This avoids several common pitfalls:

  1. It lets the kernel pick a random open port, so your test suite doesn't depend on any particular port being open.

  2. It waits for the server to be accepting connections on that port before start returns, so there's no race condition where the incoming connection arrives before the server is ready.

  3. It uses the Listener object to find out which port was picked, so it can connect to the right place.

参数:
  • handler (Callable[[trio.SocketStream], Awaitable[object]]) -- The handler to start for each incoming connection. Passed to serve_listeners().

  • port (int) -- The port to listen on. Use 0 to let the kernel pick an open port. Passed to open_tcp_listeners().

  • host (str, bytes, or None) -- The host interface to listen on; use None to bind to the wildcard address. Passed to open_tcp_listeners().

  • backlog (int | None) -- The listen backlog, or None to have a good default picked. Passed to open_tcp_listeners().

  • handler_nursery (trio.Nursery | None) -- The nursery to start handlers in, or None to use an internal nursery. Passed to serve_listeners().

  • task_status (TaskStatus[list[trio.SocketListener]]) -- This function can be used with nursery.start.

返回类型:

None

返回:

This function only returns when cancelled.

await trio.open_ssl_over_tcp_stream(host, port, *, https_compatible=False, ssl_context=None, happy_eyeballs_delay=0.25)

Make a TLS-encrypted Connection to the given host and port over TCP.

This is a convenience wrapper that calls open_tcp_stream() and wraps the result in an SSLStream.

This function does not perform the TLS handshake; you can do it manually by calling do_handshake(), or else it will be performed automatically the first time you send or receive data.

参数:
  • host (bytes or str) -- The host to connect to. We require the server to have a TLS certificate valid for this hostname.

  • port (int) -- The port to connect to.

  • https_compatible (bool) -- Set this to True if you're connecting to a web server. See SSLStream for details. Default: False.

  • ssl_context (SSLContext or None) -- The SSL context to use. If None (the default), ssl.create_default_context() will be called to create a context.

  • happy_eyeballs_delay (float) -- See open_tcp_stream().

返回:

the encrypted connection to the server.

返回类型:

trio.SSLStream

await trio.serve_ssl_over_tcp(handler, port, ssl_context, *, host=None, https_compatible=False, backlog=None, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming TCP connections, and for each one start a task running handler(stream).

This is a thin convenience wrapper around open_ssl_over_tcp_listeners() and serve_listeners() – see them for full details.

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

When used with nursery.start you get back the newly opened listeners. See the documentation for serve_tcp() for an example where this is useful.

参数:
  • handler (Callable[[trio.SSLStream[SocketStream]], Awaitable[object]]) -- The handler to start for each incoming connection. Passed to serve_listeners().

  • port (int) -- The port to listen on. Use 0 to let the kernel pick an open port. Ultimately passed to open_tcp_listeners().

  • ssl_context (SSLContext) -- The SSL context to use for all incoming connections. Passed to open_ssl_over_tcp_listeners().

  • host (str, bytes, or None) -- The address to bind to; use None to bind to the wildcard address. Ultimately passed to open_tcp_listeners().

  • https_compatible (bool) -- Set this to True if you want to use "HTTPS-style" TLS. See SSLStream for details.

  • backlog (int or None) -- See SSLStream for details.

  • handler_nursery (trio.Nursery | None) -- The nursery to start handlers in, or None to use an internal nursery. Passed to serve_listeners().

  • task_status (trio.TaskStatus[list[trio.SSLListener[SocketStream]]]) -- This function can be used with nursery.start.

返回类型:

NoReturn

返回:

This function only returns when cancelled.

await trio.open_unix_socket(filename)

Opens a connection to the specified Unix domain socket.

You must have read/write permission on the specified file to connect.

参数:

filename (str or bytes) -- The filename to open the connection to.

返回:

a Stream connected to the given file.

返回类型:

SocketStream

抛出:
  • OSError -- If the socket file could not be connected to.

  • RuntimeError -- If AF_UNIX sockets are not supported.

class trio.SocketStream(socket)

基类:HalfCloseableStream

An implementation of the trio.abc.HalfCloseableStream interface based on a raw network socket.

参数:

socket (SocketType) -- The Trio socket object to wrap. Must have type SOCK_STREAM, and be connected.

By default for TCP sockets, SocketStream enables TCP_NODELAY, and (on platforms where it's supported) enables TCP_NOTSENT_LOWAT with a reasonable buffer size (currently 16 KiB) – see issue #72 for discussion. You can of course override these defaults by calling setsockopt().

Once a SocketStream object is constructed, it implements the full trio.abc.HalfCloseableStream interface. In addition, it provides a few extra features:

socket

The Trio socket object that this stream wraps.

await aclose()
返回类型:

None

getsockopt(level, option, buffersize=0)

Check the current value of an option on the underlying socket.

See socket.socket.getsockopt() for details.

返回类型:

int | bytes

await receive_some(max_bytes=None)
返回类型:

bytes

await send_all(data)
返回类型:

None

await send_eof()
返回类型:

None

setsockopt(level, option, value, length=None)

Set an option on the underlying socket.

See socket.socket.setsockopt() for details.

返回类型:

None

await wait_send_all_might_not_block()
返回类型:

None

class trio.SocketListener(socket)

基类:Listener[SocketStream]

A Listener that uses a listening socket to accept incoming connections as SocketStream objects.

参数:

socket (SocketType) -- The Trio socket object to wrap. Must have type SOCK_STREAM, and be listening.

Note that the SocketListener "takes ownership" of the given socket; closing the SocketListener will also close the socket.

socket

The Trio socket object that this stream wraps.

await accept()

Accept an incoming connection.

返回类型:

SocketStream

返回:

SocketStream

抛出:
  • OSError -- if the underlying call to accept raises an unexpected error.

  • ClosedResourceError -- if you already closed the socket.

This method handles routine errors like ECONNABORTED, but passes other errors on to its caller. In particular, it does not make any special effort to handle resource exhaustion errors like EMFILE, ENFILE, ENOBUFS, ENOMEM.

await aclose()

Close this listener and its underlying socket.

返回类型:

None

await trio.open_tcp_listeners(port, *, host=None, backlog=None)

Create SocketListener objects to listen for TCP connections.

参数:
  • port (int) --

    The port to listen on.

    If you use 0 as your port, then the kernel will automatically pick an arbitrary open port. But be careful: if you use this feature when binding to multiple IP addresses, then each IP address will get its own random port, and the returned listeners will probably be listening on different ports. In particular, this will happen if you use host=None – which is the default – because in this case open_tcp_listeners() will bind to both the IPv4 wildcard address (0.0.0.0) and also the IPv6 wildcard address (::).

  • host (str, bytes, or None) --

    The local interface to bind to. This is passed to getaddrinfo() with the AI_PASSIVE flag set.

    If you want to bind to the wildcard address on both IPv4 and IPv6, in order to accept connections on all available interfaces, then pass None. This is the default.

    If you have a specific interface you want to bind to, pass its IP address or hostname here. If a hostname resolves to multiple IP addresses, this function will open one listener on each of them.

    If you want to use only IPv4, or only IPv6, but want to accept on all interfaces, pass the family-specific wildcard address: "0.0.0.0" for IPv4-only and "::" for IPv6-only.

  • backlog (int or None) -- The listen backlog to use. If you leave this as None then Trio will pick a good default. (Currently: whatever your system has configured as the maximum backlog.)

返回类型:

list[SocketListener]

返回:

list of SocketListener

抛出:

TypeError --

await trio.open_ssl_over_tcp_listeners(port, ssl_context, *, host=None, https_compatible=False, backlog=None)

Start listening for SSL/TLS-encrypted TCP connections to the given port.

参数:
返回类型:

list[trio.SSLListener[SocketStream]]

The high-level network interface is built on top of our stream abstraction.

await trio.open_tcp_stream(host, port, *, happy_eyeballs_delay=0.25, local_address=None)

Connect to the given host and port over TCP.

If the given host has multiple IP addresses associated with it, then we have a problem: which one do we use?

One approach would be to attempt to connect to the first one, and then if that fails, attempt to connect to the second one ... until we've tried all of them. But the problem with this is that if the first IP address is unreachable (for example, because it's an IPv6 address and our network discards IPv6 packets), then we might end up waiting tens of seconds for the first connection attempt to timeout before we try the second address.

Another approach would be to attempt to connect to all of the addresses at the same time, in parallel, and then use whichever connection succeeds first, abandoning the others. This would be fast, but create a lot of unnecessary load on the network and the remote server.

This function strikes a balance between these two extremes: it works its way through the available addresses one at a time, like the first approach; but, if happy_eyeballs_delay seconds have passed and it's still waiting for an attempt to succeed or fail, then it gets impatient and starts the next connection attempt in parallel. As soon as any one connection attempt succeeds, all the other attempts are cancelled. This avoids unnecessary load because most connections will succeed after just one or two attempts, but if one of the addresses is unreachable then it doesn't slow us down too much.

This is known as a "happy eyeballs" algorithm, and our particular variant is modelled after how Chrome connects to webservers; see RFC 6555 for more details.

参数:
  • host (str or bytes) -- The host to connect to. Can be an IPv4 address, IPv6 address, or a hostname.

  • port (int) -- The port to connect to.

  • happy_eyeballs_delay (float or None) -- How many seconds to wait for each connection attempt to succeed or fail before getting impatient and starting another one in parallel. Set to None if you want to limit to only one connection attempt at a time (like socket.create_connection()). Default: 0.25 (250 ms).

  • local_address (None or str) --

    The local IP address or hostname to use as the source for outgoing connections. If None, we let the OS pick the source IP.

    This is useful in some exotic networking configurations where your host has multiple IP addresses, and you want to force the use of a specific one.

    Note that if you pass an IPv4 local_address, then you won't be able to connect to IPv6 hosts, and vice-versa. If you want to take advantage of this to force the use of IPv4 or IPv6 without specifying an exact source address, you can use the IPv4 wildcard address local_address="0.0.0.0", or the IPv6 wildcard address local_address="::".

返回:

a Stream connected to the given server.

返回类型:

SocketStream

抛出:

OSError -- if the connection fails.

参见

open_ssl_over_tcp_stream

await trio.serve_tcp(handler, port, *, host=None, backlog=None, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming TCP connections, and for each one start a task running handler(stream).

This is a thin convenience wrapper around open_tcp_listeners() and serve_listeners() – see them for full details.

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

When used with nursery.start you get back the newly opened listeners. So, for example, if you want to start a server in your test suite and then connect to it to check that it's working properly, you can use something like:

from trio import SocketListener, SocketStream
from trio.testing import open_stream_to_socket_listener

async with trio.open_nursery() as nursery:
    listeners: list[SocketListener] = await nursery.start(serve_tcp, handler, 0)
    client_stream: SocketStream = await open_stream_to_socket_listener(listeners[0])

    # Then send and receive data on 'client_stream', for example:
    await client_stream.send_all(b"GET / HTTP/1.0\r\n\r\n")

This avoids several common pitfalls:

  1. It lets the kernel pick a random open port, so your test suite doesn't depend on any particular port being open.

  2. It waits for the server to be accepting connections on that port before start returns, so there's no race condition where the incoming connection arrives before the server is ready.

  3. It uses the Listener object to find out which port was picked, so it can connect to the right place.

参数:
  • handler (Callable[[trio.SocketStream], Awaitable[object]]) -- The handler to start for each incoming connection. Passed to serve_listeners().

  • port (int) -- The port to listen on. Use 0 to let the kernel pick an open port. Passed to open_tcp_listeners().

  • host (str, bytes, or None) -- The host interface to listen on; use None to bind to the wildcard address. Passed to open_tcp_listeners().

  • backlog (int | None) -- The listen backlog, or None to have a good default picked. Passed to open_tcp_listeners().

  • handler_nursery (trio.Nursery | None) -- The nursery to start handlers in, or None to use an internal nursery. Passed to serve_listeners().

  • task_status (TaskStatus[list[trio.SocketListener]]) -- This function can be used with nursery.start.

返回类型:

None

返回:

This function only returns when cancelled.

await trio.open_ssl_over_tcp_stream(host, port, *, https_compatible=False, ssl_context=None, happy_eyeballs_delay=0.25)

Make a TLS-encrypted Connection to the given host and port over TCP.

This is a convenience wrapper that calls open_tcp_stream() and wraps the result in an SSLStream.

This function does not perform the TLS handshake; you can do it manually by calling do_handshake(), or else it will be performed automatically the first time you send or receive data.

参数:
  • host (bytes or str) -- The host to connect to. We require the server to have a TLS certificate valid for this hostname.

  • port (int) -- The port to connect to.

  • https_compatible (bool) -- Set this to True if you're connecting to a web server. See SSLStream for details. Default: False.

  • ssl_context (SSLContext or None) -- The SSL context to use. If None (the default), ssl.create_default_context() will be called to create a context.

  • happy_eyeballs_delay (float) -- See open_tcp_stream().

返回:

the encrypted connection to the server.

返回类型:

trio.SSLStream

await trio.serve_ssl_over_tcp(handler, port, ssl_context, *, host=None, https_compatible=False, backlog=None, handler_nursery=None, task_status=TASK_STATUS_IGNORED)

Listen for incoming TCP connections, and for each one start a task running handler(stream).

This is a thin convenience wrapper around open_ssl_over_tcp_listeners() and serve_listeners() – see them for full details.

警告

If handler raises an exception, then this function doesn't do anything special to catch it – so by default the exception will propagate out and crash your server. If you don't want this, then catch exceptions inside your handler, or use a handler_nursery object that responds to exceptions in some other way.

When used with nursery.start you get back the newly opened listeners. See the documentation for serve_tcp() for an example where this is useful.

参数:
  • handler (Callable[[trio.SSLStream[SocketStream]], Awaitable[object]]) -- The handler to start for each incoming connection. Passed to serve_listeners().

  • port (int) -- The port to listen on. Use 0 to let the kernel pick an open port. Ultimately passed to open_tcp_listeners().

  • ssl_context (SSLContext) -- The SSL context to use for all incoming connections. Passed to open_ssl_over_tcp_listeners().

  • host (str, bytes, or None) -- The address to bind to; use None to bind to the wildcard address. Ultimately passed to open_tcp_listeners().

  • https_compatible (bool) -- Set this to True if you want to use "HTTPS-style" TLS. See SSLStream for details.

  • backlog (int or None) -- See SSLStream for details.

  • handler_nursery (trio.Nursery | None) -- The nursery to start handlers in, or None to use an internal nursery. Passed to serve_listeners().

  • task_status (trio.TaskStatus[list[trio.SSLListener[SocketStream]]]) -- This function can be used with nursery.start.

返回类型:

NoReturn

返回:

This function only returns when cancelled.

await trio.open_unix_socket(filename)

Opens a connection to the specified Unix domain socket.

You must have read/write permission on the specified file to connect.

参数:

filename (str or bytes) -- The filename to open the connection to.

返回:

a Stream connected to the given file.

返回类型:

SocketStream

抛出:
  • OSError -- If the socket file could not be connected to.

  • RuntimeError -- If AF_UNIX sockets are not supported.

class trio.SocketStream(socket)

基类:HalfCloseableStream

An implementation of the trio.abc.HalfCloseableStream interface based on a raw network socket.

参数:

socket (SocketType) -- The Trio socket object to wrap. Must have type SOCK_STREAM, and be connected.

By default for TCP sockets, SocketStream enables TCP_NODELAY, and (on platforms where it's supported) enables TCP_NOTSENT_LOWAT with a reasonable buffer size (currently 16 KiB) – see issue #72 for discussion. You can of course override these defaults by calling setsockopt().

Once a SocketStream object is constructed, it implements the full trio.abc.HalfCloseableStream interface. In addition, it provides a few extra features:

socket

The Trio socket object that this stream wraps.

await aclose()
返回类型:

None

getsockopt(level, option, buffersize=0)

Check the current value of an option on the underlying socket.

See socket.socket.getsockopt() for details.

返回类型:

int | bytes

await receive_some(max_bytes=None)
返回类型:

bytes

await send_all(data)
返回类型:

None

await send_eof()
返回类型:

None

setsockopt(level, option, value, length=None)

Set an option on the underlying socket.

See socket.socket.setsockopt() for details.

返回类型:

None

await wait_send_all_might_not_block()
返回类型:

None

class trio.SocketListener(socket)

基类:Listener[SocketStream]

A Listener that uses a listening socket to accept incoming connections as SocketStream objects.

参数:

socket (SocketType) -- The Trio socket object to wrap. Must have type SOCK_STREAM, and be listening.

Note that the SocketListener "takes ownership" of the given socket; closing the SocketListener will also close the socket.

socket

The Trio socket object that this stream wraps.

await accept()

Accept an incoming connection.

返回类型:

SocketStream

返回:

SocketStream

抛出:
  • OSError -- if the underlying call to accept raises an unexpected error.

  • ClosedResourceError -- if you already closed the socket.

This method handles routine errors like ECONNABORTED, but passes other errors on to its caller. In particular, it does not make any special effort to handle resource exhaustion errors like EMFILE, ENFILE, ENOBUFS, ENOMEM.

await aclose()

Close this listener and its underlying socket.

返回类型:

None

await trio.open_tcp_listeners(port, *, host=None, backlog=None)

Create SocketListener objects to listen for TCP connections.

参数:
  • port (int) --

    The port to listen on.

    If you use 0 as your port, then the kernel will automatically pick an arbitrary open port. But be careful: if you use this feature when binding to multiple IP addresses, then each IP address will get its own random port, and the returned listeners will probably be listening on different ports. In particular, this will happen if you use host=None – which is the default – because in this case open_tcp_listeners() will bind to both the IPv4 wildcard address (0.0.0.0) and also the IPv6 wildcard address (::).

  • host (str, bytes, or None) --

    The local interface to bind to. This is passed to getaddrinfo() with the AI_PASSIVE flag set.

    If you want to bind to the wildcard address on both IPv4 and IPv6, in order to accept connections on all available interfaces, then pass None. This is the default.

    If you have a specific interface you want to bind to, pass its IP address or hostname here. If a hostname resolves to multiple IP addresses, this function will open one listener on each of them.

    If you want to use only IPv4, or only IPv6, but want to accept on all interfaces, pass the family-specific wildcard address: "0.0.0.0" for IPv4-only and "::" for IPv6-only.

  • backlog (int or None) -- The listen backlog to use. If you leave this as None then Trio will pick a good default. (Currently: whatever your system has configured as the maximum backlog.)

返回类型:

list[SocketListener]

返回:

list of SocketListener

抛出:

TypeError --

await trio.open_ssl_over_tcp_listeners(port, ssl_context, *, host=None, https_compatible=False, backlog=None)

Start listening for SSL/TLS-encrypted TCP connections to the given port.

参数:
返回类型:

list[trio.SSLListener[SocketStream]]

SSL / TLS 支持

SSL / TLS support

Trio 提供了基于标准库 ssl 模块的 SSL/TLS 支持。Trio 的 SSLStreamSSLListenerssl.SSLContext 获取其配置, 您可以使用 ssl.create_default_context() 创建该上下文, 并使用 ssl 模块中的其他常量和函数进行自定义。

警告

避免直接实例化 ssl.SSLContext。 新创建的 ssl.SSLContext 的默认配置不如通过 ssl.create_default_context() 返回的上下文安全。

您可以通过创建 SSLStream 来代替使用 ssl.SSLContext.wrap_socket()

class trio.SSLStream(transport_stream, ssl_context, *, server_hostname=None, server_side=False, https_compatible=False)

基类:Stream, Generic[T_Stream]

Encrypted communication using SSL/TLS.

SSLStream wraps an arbitrary Stream, and allows you to perform encrypted communication over it using the usual Stream interface. You pass regular data to send_all(), then it encrypts it and sends the encrypted data on the underlying Stream; receive_some() takes encrypted data out of the underlying Stream and decrypts it before returning it.

You should read the standard library's ssl documentation carefully before attempting to use this class, and probably other general documentation on SSL/TLS as well. SSL/TLS is subtle and quick to anger. Really. I'm not kidding.

参数:
  • transport_stream (Stream) -- The stream used to transport encrypted data. Required.

  • ssl_context (SSLContext) -- The SSLContext used for this connection. Required. Usually created by calling ssl.create_default_context().

  • server_hostname (str, bytes, or None) -- The name of the server being connected to. Used for SNI and for validating the server's certificate (if hostname checking is enabled). This is effectively mandatory for clients, and actually mandatory if ssl_context.check_hostname is True.

  • server_side (bool) -- Whether this stream is acting as a client or server. Defaults to False, i.e. client mode.

  • https_compatible (bool) --

    There are two versions of SSL/TLS commonly encountered in the wild: the standard version, and the version used for HTTPS (HTTP-over-SSL/TLS).

    Standard-compliant SSL/TLS implementations always send a cryptographically signed close_notify message before closing the connection. This is important because if the underlying transport were simply closed, then there wouldn't be any way for the other side to know whether the connection was intentionally closed by the peer that they negotiated a cryptographic connection to, or by some man-in-the-middle attacker who can't manipulate the cryptographic stream, but can manipulate the transport layer (a so-called "truncation attack").

    However, this part of the standard is widely ignored by real-world HTTPS implementations, which means that if you want to interoperate with them, then you NEED to ignore it too.

    Fortunately this isn't as bad as it sounds, because the HTTP protocol already includes its own equivalent of close_notify, so doing this again at the SSL/TLS level is redundant. But not all protocols do! Therefore, by default Trio implements the safer standard-compliant version (https_compatible=False). But if you're speaking HTTPS or some other protocol where close_notifys are commonly skipped, then you should set https_compatible=True; with this setting, Trio will neither expect nor send close_notify messages.

    If you have code that was written to use ssl.SSLSocket and now you're porting it to Trio, then it may be useful to know that a difference between SSLStream and ssl.SSLSocket is that SSLSocket implements the https_compatible=True behavior by default.

transport_stream

The underlying transport stream that was passed to __init__. An example of when this would be useful is if you're using SSLStream over a SocketStream and want to call the SocketStream's setsockopt() method.

Type:

trio.abc.Stream

Internally, this class is implemented using an instance of ssl.SSLObject, and all of SSLObject's methods and attributes are re-exported as methods and attributes on this class. However, there is one difference: SSLObject has several methods that return information about the encrypted connection, like cipher() or selected_alpn_protocol(). If you call them before the handshake, when they can't possibly return useful data, then ssl.SSLObject returns None, but trio.SSLStream raises NeedHandshakeError.

This also means that if you register a SNI callback using ~ssl.SSLContext.sni_callback, then the first argument your callback receives will be a ssl.SSLObject.

await aclose()

Gracefully shut down this connection, and close the underlying transport.

If https_compatible is False (the default), then this attempts to first send a close_notify and then close the underlying stream by calling its aclose() method.

If https_compatible is set to True, then this simply closes the underlying stream and marks this stream as closed.

返回类型:

None

await do_handshake()

Ensure that the initial handshake has completed.

The SSL protocol requires an initial handshake to exchange certificates, select cryptographic keys, and so forth, before any actual data can be sent or received. You don't have to call this method; if you don't, then SSLStream will automatically perform the handshake as needed, the first time you try to send or receive data. But if you want to trigger it manually – for example, because you want to look at the peer's certificate before you start talking to them – then you can call this method.

If the initial handshake is already in progress in another task, this waits for it to complete and then returns.

If the initial handshake has already completed, this returns immediately without doing anything (except executing a checkpoint). :rtype: None

警告

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenResourceError.

await receive_some(max_bytes=None)

Read some data from the underlying transport, decrypt it, and return it.

See trio.abc.ReceiveStream.receive_some() for details. :rtype: bytes | bytearray

警告

If this method is cancelled while the initial handshake or a renegotiation are in progress, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenResourceError.

await send_all(data)

Encrypt some data and then send it on the underlying transport.

See trio.abc.SendStream.send_all() for details. :rtype: None

警告

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any attempt to use the object will raise trio.BrokenResourceError.

await unwrap()

Cleanly close down the SSL/TLS encryption layer, allowing the underlying stream to be used for unencrypted communication.

You almost certainly don't need this.

返回类型:

tuple[Stream, bytes | bytearray]

返回:

A pair (transport_stream, trailing_bytes), where transport_stream is the underlying transport stream, and trailing_bytes is a byte string. Since SSLStream doesn't necessarily know where the end of the encrypted data will be, it can happen that it accidentally reads too much from the underlying stream. trailing_bytes contains this extra data; you should process it as if it was returned from a call to transport_stream.receive_some(...).

await wait_send_all_might_not_block()

See trio.abc.SendStream.wait_send_all_might_not_block().

返回类型:

None

如果您正在实现一个服务器, 您可以使用 SSLListener

class trio.SSLListener(transport_listener, ssl_context, *, https_compatible=False)

基类:Listener[SSLStream[T_Stream]]

A Listener for SSL/TLS-encrypted servers.

SSLListener wraps around another Listener, and converts all incoming connections to encrypted connections by wrapping them in a SSLStream.

参数:
transport_listener

The underlying listener that was passed to __init__.

Type:

trio.abc.Listener

await accept()

Accept the next connection and wrap it in an SSLStream.

See trio.abc.Listener.accept() for details.

await aclose()

Close the transport listener.

返回类型:

None

SSLStream 上的某些方法如果在握手完成之前调用, 将会引发 NeedHandshakeError

exception trio.NeedHandshakeError

基类:Exception

Some SSLStream methods can't return any meaningful data until after the handshake. If you call them before the handshake, they raise this error.

Trio provides SSL/TLS support based on the standard library ssl module. Trio's SSLStream and SSLListener take their configuration from a ssl.SSLContext, which you can create using ssl.create_default_context() and customize using the other constants and functions in the ssl module.

警告

Avoid instantiating ssl.SSLContext directly. A newly constructed SSLContext has less secure defaults than one returned by ssl.create_default_context().

Instead of using ssl.SSLContext.wrap_socket(), you create a SSLStream:

class trio.SSLStream(transport_stream, ssl_context, *, server_hostname=None, server_side=False, https_compatible=False)

基类:Stream, Generic[T_Stream]

Encrypted communication using SSL/TLS.

SSLStream wraps an arbitrary Stream, and allows you to perform encrypted communication over it using the usual Stream interface. You pass regular data to send_all(), then it encrypts it and sends the encrypted data on the underlying Stream; receive_some() takes encrypted data out of the underlying Stream and decrypts it before returning it.

You should read the standard library's ssl documentation carefully before attempting to use this class, and probably other general documentation on SSL/TLS as well. SSL/TLS is subtle and quick to anger. Really. I'm not kidding.

参数:
  • transport_stream (Stream) -- The stream used to transport encrypted data. Required.

  • ssl_context (SSLContext) -- The SSLContext used for this connection. Required. Usually created by calling ssl.create_default_context().

  • server_hostname (str, bytes, or None) -- The name of the server being connected to. Used for SNI and for validating the server's certificate (if hostname checking is enabled). This is effectively mandatory for clients, and actually mandatory if ssl_context.check_hostname is True.

  • server_side (bool) -- Whether this stream is acting as a client or server. Defaults to False, i.e. client mode.

  • https_compatible (bool) --

    There are two versions of SSL/TLS commonly encountered in the wild: the standard version, and the version used for HTTPS (HTTP-over-SSL/TLS).

    Standard-compliant SSL/TLS implementations always send a cryptographically signed close_notify message before closing the connection. This is important because if the underlying transport were simply closed, then there wouldn't be any way for the other side to know whether the connection was intentionally closed by the peer that they negotiated a cryptographic connection to, or by some man-in-the-middle attacker who can't manipulate the cryptographic stream, but can manipulate the transport layer (a so-called "truncation attack").

    However, this part of the standard is widely ignored by real-world HTTPS implementations, which means that if you want to interoperate with them, then you NEED to ignore it too.

    Fortunately this isn't as bad as it sounds, because the HTTP protocol already includes its own equivalent of close_notify, so doing this again at the SSL/TLS level is redundant. But not all protocols do! Therefore, by default Trio implements the safer standard-compliant version (https_compatible=False). But if you're speaking HTTPS or some other protocol where close_notifys are commonly skipped, then you should set https_compatible=True; with this setting, Trio will neither expect nor send close_notify messages.

    If you have code that was written to use ssl.SSLSocket and now you're porting it to Trio, then it may be useful to know that a difference between SSLStream and ssl.SSLSocket is that SSLSocket implements the https_compatible=True behavior by default.

transport_stream

The underlying transport stream that was passed to __init__. An example of when this would be useful is if you're using SSLStream over a SocketStream and want to call the SocketStream's setsockopt() method.

Type:

trio.abc.Stream

Internally, this class is implemented using an instance of ssl.SSLObject, and all of SSLObject's methods and attributes are re-exported as methods and attributes on this class. However, there is one difference: SSLObject has several methods that return information about the encrypted connection, like cipher() or selected_alpn_protocol(). If you call them before the handshake, when they can't possibly return useful data, then ssl.SSLObject returns None, but trio.SSLStream raises NeedHandshakeError.

This also means that if you register a SNI callback using ~ssl.SSLContext.sni_callback, then the first argument your callback receives will be a ssl.SSLObject.

await aclose()

Gracefully shut down this connection, and close the underlying transport.

If https_compatible is False (the default), then this attempts to first send a close_notify and then close the underlying stream by calling its aclose() method.

If https_compatible is set to True, then this simply closes the underlying stream and marks this stream as closed.

返回类型:

None

await do_handshake()

Ensure that the initial handshake has completed.

The SSL protocol requires an initial handshake to exchange certificates, select cryptographic keys, and so forth, before any actual data can be sent or received. You don't have to call this method; if you don't, then SSLStream will automatically perform the handshake as needed, the first time you try to send or receive data. But if you want to trigger it manually – for example, because you want to look at the peer's certificate before you start talking to them – then you can call this method.

If the initial handshake is already in progress in another task, this waits for it to complete and then returns.

If the initial handshake has already completed, this returns immediately without doing anything (except executing a checkpoint). :rtype: None

警告

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenResourceError.

await receive_some(max_bytes=None)

Read some data from the underlying transport, decrypt it, and return it.

See trio.abc.ReceiveStream.receive_some() for details. :rtype: bytes | bytearray

警告

If this method is cancelled while the initial handshake or a renegotiation are in progress, then it may leave the SSLStream in an unusable state. If this happens then any future attempt to use the object will raise trio.BrokenResourceError.

await send_all(data)

Encrypt some data and then send it on the underlying transport.

See trio.abc.SendStream.send_all() for details. :rtype: None

警告

If this method is cancelled, then it may leave the SSLStream in an unusable state. If this happens then any attempt to use the object will raise trio.BrokenResourceError.

await unwrap()

Cleanly close down the SSL/TLS encryption layer, allowing the underlying stream to be used for unencrypted communication.

You almost certainly don't need this.

返回类型:

tuple[Stream, bytes | bytearray]

返回:

A pair (transport_stream, trailing_bytes), where transport_stream is the underlying transport stream, and trailing_bytes is a byte string. Since SSLStream doesn't necessarily know where the end of the encrypted data will be, it can happen that it accidentally reads too much from the underlying stream. trailing_bytes contains this extra data; you should process it as if it was returned from a call to transport_stream.receive_some(...).

await wait_send_all_might_not_block()

See trio.abc.SendStream.wait_send_all_might_not_block().

返回类型:

None

And if you're implementing a server, you can use SSLListener:

class trio.SSLListener(transport_listener, ssl_context, *, https_compatible=False)

基类:Listener[SSLStream[T_Stream]]

A Listener for SSL/TLS-encrypted servers.

SSLListener wraps around another Listener, and converts all incoming connections to encrypted connections by wrapping them in a SSLStream.

参数:
transport_listener

The underlying listener that was passed to __init__.

Type:

trio.abc.Listener

await accept()

Accept the next connection and wrap it in an SSLStream.

See trio.abc.Listener.accept() for details.

await aclose()

Close the transport listener.

返回类型:

None

Some methods on SSLStream raise NeedHandshakeError if you call them before the handshake completes:

exception trio.NeedHandshakeError

基类:Exception

Some SSLStream methods can't return any meaningful data until after the handshake. If you call them before the handshake, they raise this error.

数据报 TLS 支持

Datagram TLS support

Trio 还支持数据报 TLS (DTLS ) , 它类似于 TLS, 但用于不可靠的 UDP 连接。对于那些 TCP 的可靠顺序交付存在问题的应用程序, 如视频会议, 延迟敏感的游戏和 VPN, 这非常有用。

目前, 使用 DTLS 与 Trio 需要 PyOpenSSL。我们希望最终能够使用标准库的 ssl 模块, 但不幸的是, 目前尚不可能。

警告

请注意, PyOpenSSL 在许多方面比 ssl 模块更底层——尤其是, 它当前 没有内置的机制来验证证书。我们 强烈 推荐您使用 service-identity 库来验证主机名和证书。

class trio.DTLSEndpoint(socket, *, incoming_packets_buffer=10)

基类:object

A DTLS endpoint.

A single UDP socket can handle arbitrarily many DTLS connections simultaneously, acting as a client or server as needed. A DTLSEndpoint object holds a UDP socket and manages these connections, which are represented as DTLSChannel objects.

参数:
  • socket (SocketType) -- (trio.socket.SocketType): A SOCK_DGRAM socket. If you want to accept incoming connections in server mode, then you should probably bind the socket to some known port.

  • incoming_packets_buffer (int) -- Each DTLSChannel using this socket has its own buffer that holds incoming packets until you call ~DTLSChannel.receive to read them. This lets you adjust the size of this buffer. ~DTLSChannel.statistics lets you check if the buffer has overflowed.

socket
incoming_packets_buffer

Both constructor arguments are also exposed as attributes, in case you need to access them later.

connect(address, ssl_context)

Initiate an outgoing DTLS connection.

Notice that this is a synchronous method. That's because it doesn't actually initiate any I/O – it just sets up a DTLSChannel object. The actual handshake doesn't occur until you start using the DTLSChannel. This gives you a chance to do further configuration first, like setting MTU etc.

参数:
  • address (tuple[str, int]) -- The address to connect to. Usually a (host, port) tuple, like ("127.0.0.1", 12345).

  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for this connection.

返回类型:

DTLSChannel

返回:

DTLSChannel

await serve(ssl_context, async_fn, *args, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections, and spawn a handler for each using an internal nursery.

Similar to ~trio.serve_tcp, this function never returns until cancelled, or the DTLSEndpoint is closed and all handlers have exited.

Usage commonly looks like:

async def handler(dtls_channel):
    ...

async with trio.open_nursery() as nursery:
    await nursery.start(dtls_endpoint.serve, ssl_context, handler)
    # ... do other things here ...

The dtls_channel passed into the handler function has already performed the "cookie exchange" part of the DTLS handshake, so the peer address is trustworthy. But the actual cryptographic handshake doesn't happen until you start using it, giving you a chance for any last minute configuration, and the option to catch and handle handshake errors.

参数:
  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for incoming connections.

  • async_fn (Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]]) -- The handler function that will be invoked for each incoming connection.

  • *args (Unpack[PosArgsT]) -- Additional arguments to pass to the handler function.

返回类型:

None

close()

Close this socket, and all associated DTLS connections.

This object can also be used as a context manager.

返回类型:

None

close()

Close this socket, and all associated DTLS connections.

This object can also be used as a context manager.

返回类型:

None

connect(address, ssl_context)

Initiate an outgoing DTLS connection.

Notice that this is a synchronous method. That's because it doesn't actually initiate any I/O – it just sets up a DTLSChannel object. The actual handshake doesn't occur until you start using the DTLSChannel. This gives you a chance to do further configuration first, like setting MTU etc.

参数:
  • address (tuple[str, int]) -- The address to connect to. Usually a (host, port) tuple, like ("127.0.0.1", 12345).

  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for this connection.

返回类型:

DTLSChannel

返回:

DTLSChannel

await serve(ssl_context, async_fn, *args, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections, and spawn a handler for each using an internal nursery.

Similar to ~trio.serve_tcp, this function never returns until cancelled, or the DTLSEndpoint is closed and all handlers have exited.

Usage commonly looks like:

async def handler(dtls_channel):
    ...

async with trio.open_nursery() as nursery:
    await nursery.start(dtls_endpoint.serve, ssl_context, handler)
    # ... do other things here ...

The dtls_channel passed into the handler function has already performed the "cookie exchange" part of the DTLS handshake, so the peer address is trustworthy. But the actual cryptographic handshake doesn't happen until you start using it, giving you a chance for any last minute configuration, and the option to catch and handle handshake errors.

参数:
  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for incoming connections.

  • async_fn (Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]]) -- The handler function that will be invoked for each incoming connection.

  • *args (Unpack[PosArgsT]) -- Additional arguments to pass to the handler function.

返回类型:

None

class trio.DTLSChannel(endpoint, peer_address, ctx)

基类:Channel[bytes]

A DTLS connection.

This class has no public constructor – you get instances by calling DTLSEndpoint.serve or ~DTLSEndpoint.connect.

endpoint

The DTLSEndpoint that this connection is using.

peer_address

The IP/port of the remote peer that this connection is associated with.

await do_handshake(*, initial_retransmit_timeout=1.0)

Perform the handshake.

Calling this is optional – if you don't, then it will be automatically called the first time you call send or receive. But calling it explicitly can be useful in case you want to control the retransmit timeout, use a cancel scope to place an overall timeout on the handshake, or catch errors from the handshake specifically.

It's safe to call this multiple times, or call it simultaneously from multiple tasks – the first call will perform the handshake, and the rest will be no-ops.

参数:

initial_retransmit_timeout (float) --

Since UDP is an unreliable protocol, it's possible that some of the packets we send during the handshake will get lost. To handle this, DTLS uses a timer to automatically retransmit handshake packets that don't receive a response. This lets you set the timeout we use to detect packet loss. Ideally, it should be set to ~1.5 times the round-trip time to your peer, but 1 second is a reasonable default. There's some useful guidance here.

This is the initial timeout, because if packets keep being lost then Trio will automatically back off to longer values, to avoid overloading the network.

返回类型:

None

await send(data)

Send a packet of data, securely.

返回类型:

None

await receive()

Fetch the next packet of data from this connection's peer, waiting if necessary.

This is safe to call from multiple tasks simultaneously, in case you have some reason to do that. And more importantly, it's cancellation-safe, meaning that cancelling a call to receive will never cause a packet to be lost or corrupt the underlying connection.

返回类型:

bytes

close()

Close this connection.

DTLSChannels don't actually own any OS-level resources – the socket is owned by the DTLSEndpoint, not the individual connections. So you don't really have to call this. But it will interrupt any other tasks calling receive with a ClosedResourceError, and cause future attempts to use this connection to fail.

You can also use this object as a synchronous or asynchronous context manager.

返回类型:

None

await aclose()

Close this connection, but asynchronously.

This is included to satisfy the trio.abc.Channel contract. It's identical to close, but async.

返回类型:

None

set_ciphertext_mtu(new_mtu)

Tells Trio the largest amount of data that can be sent in a single packet to this peer.

Trio doesn't actually enforce this limit – if you pass a huge packet to send, then we'll dutifully encrypt it and attempt to send it. But calling this method does have two useful effects: :rtype: None

  • If called before the handshake is performed, then Trio will automatically fragment handshake messages to fit within the given MTU. It also might fragment them even smaller, if it detects signs of packet loss, so setting this should never be necessary to make a successful connection. But, the packet loss detection only happens after multiple timeouts have expired, so if you have reason to believe that a smaller MTU is required, then you can set this to skip those timeouts and establish the connection more quickly.

  • It changes the value returned from get_cleartext_mtu. So if you have some kind of estimate of the network-level MTU, then you can use this to figure out how much overhead DTLS will need for hashes/padding/etc., and how much space you have left for your application data.

The MTU here is measuring the largest UDP payload you think can be sent, the amount of encrypted data that can be handed to the operating system in a single call to send. It should not include IP/UDP headers. Note that OS estimates of the MTU often are link-layer MTUs, so you have to subtract off 28 bytes on IPv4 and 48 bytes on IPv6 to get the ciphertext MTU.

By default, Trio assumes an MTU of 1472 bytes on IPv4, and 1452 bytes on IPv6, which correspond to the common Ethernet MTU of 1500 bytes after accounting for IP/UDP overhead.

get_cleartext_mtu()

Returns the largest number of bytes that you can pass in a single call to send while still fitting within the network-level MTU.

See set_ciphertext_mtu for more details.

返回类型:

int

statistics()

Returns a DTLSChannelStatistics object with statistics about this connection.

返回类型:

DTLSChannelStatistics

await aclose()

Close this connection, but asynchronously.

This is included to satisfy the trio.abc.Channel contract. It's identical to close, but async.

返回类型:

None

close()

Close this connection.

DTLSChannels don't actually own any OS-level resources – the socket is owned by the DTLSEndpoint, not the individual connections. So you don't really have to call this. But it will interrupt any other tasks calling receive with a ClosedResourceError, and cause future attempts to use this connection to fail.

You can also use this object as a synchronous or asynchronous context manager.

返回类型:

None

await do_handshake(*, initial_retransmit_timeout=1.0)

Perform the handshake.

Calling this is optional – if you don't, then it will be automatically called the first time you call send or receive. But calling it explicitly can be useful in case you want to control the retransmit timeout, use a cancel scope to place an overall timeout on the handshake, or catch errors from the handshake specifically.

It's safe to call this multiple times, or call it simultaneously from multiple tasks – the first call will perform the handshake, and the rest will be no-ops.

参数:

initial_retransmit_timeout (float) --

Since UDP is an unreliable protocol, it's possible that some of the packets we send during the handshake will get lost. To handle this, DTLS uses a timer to automatically retransmit handshake packets that don't receive a response. This lets you set the timeout we use to detect packet loss. Ideally, it should be set to ~1.5 times the round-trip time to your peer, but 1 second is a reasonable default. There's some useful guidance here.

This is the initial timeout, because if packets keep being lost then Trio will automatically back off to longer values, to avoid overloading the network.

返回类型:

None

get_cleartext_mtu()

Returns the largest number of bytes that you can pass in a single call to send while still fitting within the network-level MTU.

See set_ciphertext_mtu for more details.

返回类型:

int

await receive()

Fetch the next packet of data from this connection's peer, waiting if necessary.

This is safe to call from multiple tasks simultaneously, in case you have some reason to do that. And more importantly, it's cancellation-safe, meaning that cancelling a call to receive will never cause a packet to be lost or corrupt the underlying connection.

返回类型:

bytes

await send(data)

Send a packet of data, securely.

返回类型:

None

set_ciphertext_mtu(new_mtu)

Tells Trio the largest amount of data that can be sent in a single packet to this peer.

Trio doesn't actually enforce this limit – if you pass a huge packet to send, then we'll dutifully encrypt it and attempt to send it. But calling this method does have two useful effects: :rtype: None

  • If called before the handshake is performed, then Trio will automatically fragment handshake messages to fit within the given MTU. It also might fragment them even smaller, if it detects signs of packet loss, so setting this should never be necessary to make a successful connection. But, the packet loss detection only happens after multiple timeouts have expired, so if you have reason to believe that a smaller MTU is required, then you can set this to skip those timeouts and establish the connection more quickly.

  • It changes the value returned from get_cleartext_mtu. So if you have some kind of estimate of the network-level MTU, then you can use this to figure out how much overhead DTLS will need for hashes/padding/etc., and how much space you have left for your application data.

The MTU here is measuring the largest UDP payload you think can be sent, the amount of encrypted data that can be handed to the operating system in a single call to send. It should not include IP/UDP headers. Note that OS estimates of the MTU often are link-layer MTUs, so you have to subtract off 28 bytes on IPv4 and 48 bytes on IPv6 to get the ciphertext MTU.

By default, Trio assumes an MTU of 1472 bytes on IPv4, and 1452 bytes on IPv6, which correspond to the common Ethernet MTU of 1500 bytes after accounting for IP/UDP overhead.

statistics()

Returns a DTLSChannelStatistics object with statistics about this connection.

返回类型:

DTLSChannelStatistics

class trio.DTLSChannelStatistics(incoming_packets_dropped_in_trio)

基类:object

Currently this has only one attribute:

  • incoming_packets_dropped_in_trio (int): Gives a count of the number of incoming packets from this peer that Trio successfully received from the network, but then got dropped because the internal channel buffer was full. If this is non-zero, then you might want to call receive more often, or use a larger incoming_packets_buffer, or just not worry about it because your UDP-based protocol should be able to handle the occasional lost packet, right?

Trio also has support for Datagram TLS (DTLS), which is like TLS but for unreliable UDP connections. This can be useful for applications where TCP's reliable in-order delivery is problematic, like teleconferencing, latency-sensitive games, and VPNs.

Currently, using DTLS with Trio requires PyOpenSSL. We hope to eventually allow the use of the stdlib ssl module as well, but unfortunately that's not yet possible.

警告

Note that PyOpenSSL is in many ways lower-level than the ssl module – in particular, it currently HAS NO BUILT-IN MECHANISM TO VALIDATE CERTIFICATES. We strongly recommend that you use the service-identity library to validate hostnames and certificates.

class trio.DTLSEndpoint(socket, *, incoming_packets_buffer=10)

基类:object

A DTLS endpoint.

A single UDP socket can handle arbitrarily many DTLS connections simultaneously, acting as a client or server as needed. A DTLSEndpoint object holds a UDP socket and manages these connections, which are represented as DTLSChannel objects.

参数:
  • socket (SocketType) -- (trio.socket.SocketType): A SOCK_DGRAM socket. If you want to accept incoming connections in server mode, then you should probably bind the socket to some known port.

  • incoming_packets_buffer (int) -- Each DTLSChannel using this socket has its own buffer that holds incoming packets until you call ~DTLSChannel.receive to read them. This lets you adjust the size of this buffer. ~DTLSChannel.statistics lets you check if the buffer has overflowed.

socket
incoming_packets_buffer

Both constructor arguments are also exposed as attributes, in case you need to access them later.

connect(address, ssl_context)

Initiate an outgoing DTLS connection.

Notice that this is a synchronous method. That's because it doesn't actually initiate any I/O – it just sets up a DTLSChannel object. The actual handshake doesn't occur until you start using the DTLSChannel. This gives you a chance to do further configuration first, like setting MTU etc.

参数:
  • address (tuple[str, int]) -- The address to connect to. Usually a (host, port) tuple, like ("127.0.0.1", 12345).

  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for this connection.

返回类型:

DTLSChannel

返回:

DTLSChannel

await serve(ssl_context, async_fn, *args, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections, and spawn a handler for each using an internal nursery.

Similar to ~trio.serve_tcp, this function never returns until cancelled, or the DTLSEndpoint is closed and all handlers have exited.

Usage commonly looks like:

async def handler(dtls_channel):
    ...

async with trio.open_nursery() as nursery:
    await nursery.start(dtls_endpoint.serve, ssl_context, handler)
    # ... do other things here ...

The dtls_channel passed into the handler function has already performed the "cookie exchange" part of the DTLS handshake, so the peer address is trustworthy. But the actual cryptographic handshake doesn't happen until you start using it, giving you a chance for any last minute configuration, and the option to catch and handle handshake errors.

参数:
  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for incoming connections.

  • async_fn (Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]]) -- The handler function that will be invoked for each incoming connection.

  • *args (Unpack[PosArgsT]) -- Additional arguments to pass to the handler function.

返回类型:

None

close()

Close this socket, and all associated DTLS connections.

This object can also be used as a context manager.

返回类型:

None

close()

Close this socket, and all associated DTLS connections.

This object can also be used as a context manager.

返回类型:

None

connect(address, ssl_context)

Initiate an outgoing DTLS connection.

Notice that this is a synchronous method. That's because it doesn't actually initiate any I/O – it just sets up a DTLSChannel object. The actual handshake doesn't occur until you start using the DTLSChannel. This gives you a chance to do further configuration first, like setting MTU etc.

参数:
  • address (tuple[str, int]) -- The address to connect to. Usually a (host, port) tuple, like ("127.0.0.1", 12345).

  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for this connection.

返回类型:

DTLSChannel

返回:

DTLSChannel

await serve(ssl_context, async_fn, *args, task_status=TASK_STATUS_IGNORED)

Listen for incoming connections, and spawn a handler for each using an internal nursery.

Similar to ~trio.serve_tcp, this function never returns until cancelled, or the DTLSEndpoint is closed and all handlers have exited.

Usage commonly looks like:

async def handler(dtls_channel):
    ...

async with trio.open_nursery() as nursery:
    await nursery.start(dtls_endpoint.serve, ssl_context, handler)
    # ... do other things here ...

The dtls_channel passed into the handler function has already performed the "cookie exchange" part of the DTLS handshake, so the peer address is trustworthy. But the actual cryptographic handshake doesn't happen until you start using it, giving you a chance for any last minute configuration, and the option to catch and handle handshake errors.

参数:
  • ssl_context (OpenSSL.SSL.Context) -- The PyOpenSSL context object to use for incoming connections.

  • async_fn (Callable[[DTLSChannel, Unpack[PosArgsT]], Awaitable[object]]) -- The handler function that will be invoked for each incoming connection.

  • *args (Unpack[PosArgsT]) -- Additional arguments to pass to the handler function.

返回类型:

None

class trio.DTLSChannel(endpoint, peer_address, ctx)

基类:Channel[bytes]

A DTLS connection.

This class has no public constructor – you get instances by calling DTLSEndpoint.serve or ~DTLSEndpoint.connect.

endpoint

The DTLSEndpoint that this connection is using.

peer_address

The IP/port of the remote peer that this connection is associated with.

await do_handshake(*, initial_retransmit_timeout=1.0)

Perform the handshake.

Calling this is optional – if you don't, then it will be automatically called the first time you call send or receive. But calling it explicitly can be useful in case you want to control the retransmit timeout, use a cancel scope to place an overall timeout on the handshake, or catch errors from the handshake specifically.

It's safe to call this multiple times, or call it simultaneously from multiple tasks – the first call will perform the handshake, and the rest will be no-ops.

参数:

initial_retransmit_timeout (float) --

Since UDP is an unreliable protocol, it's possible that some of the packets we send during the handshake will get lost. To handle this, DTLS uses a timer to automatically retransmit handshake packets that don't receive a response. This lets you set the timeout we use to detect packet loss. Ideally, it should be set to ~1.5 times the round-trip time to your peer, but 1 second is a reasonable default. There's some useful guidance here.

This is the initial timeout, because if packets keep being lost then Trio will automatically back off to longer values, to avoid overloading the network.

返回类型:

None

await send(data)

Send a packet of data, securely.

返回类型:

None

await receive()

Fetch the next packet of data from this connection's peer, waiting if necessary.

This is safe to call from multiple tasks simultaneously, in case you have some reason to do that. And more importantly, it's cancellation-safe, meaning that cancelling a call to receive will never cause a packet to be lost or corrupt the underlying connection.

返回类型:

bytes

close()

Close this connection.

DTLSChannels don't actually own any OS-level resources – the socket is owned by the DTLSEndpoint, not the individual connections. So you don't really have to call this. But it will interrupt any other tasks calling receive with a ClosedResourceError, and cause future attempts to use this connection to fail.

You can also use this object as a synchronous or asynchronous context manager.

返回类型:

None

await aclose()

Close this connection, but asynchronously.

This is included to satisfy the trio.abc.Channel contract. It's identical to close, but async.

返回类型:

None

set_ciphertext_mtu(new_mtu)

Tells Trio the largest amount of data that can be sent in a single packet to this peer.

Trio doesn't actually enforce this limit – if you pass a huge packet to send, then we'll dutifully encrypt it and attempt to send it. But calling this method does have two useful effects: :rtype: None

  • If called before the handshake is performed, then Trio will automatically fragment handshake messages to fit within the given MTU. It also might fragment them even smaller, if it detects signs of packet loss, so setting this should never be necessary to make a successful connection. But, the packet loss detection only happens after multiple timeouts have expired, so if you have reason to believe that a smaller MTU is required, then you can set this to skip those timeouts and establish the connection more quickly.

  • It changes the value returned from get_cleartext_mtu. So if you have some kind of estimate of the network-level MTU, then you can use this to figure out how much overhead DTLS will need for hashes/padding/etc., and how much space you have left for your application data.

The MTU here is measuring the largest UDP payload you think can be sent, the amount of encrypted data that can be handed to the operating system in a single call to send. It should not include IP/UDP headers. Note that OS estimates of the MTU often are link-layer MTUs, so you have to subtract off 28 bytes on IPv4 and 48 bytes on IPv6 to get the ciphertext MTU.

By default, Trio assumes an MTU of 1472 bytes on IPv4, and 1452 bytes on IPv6, which correspond to the common Ethernet MTU of 1500 bytes after accounting for IP/UDP overhead.

get_cleartext_mtu()

Returns the largest number of bytes that you can pass in a single call to send while still fitting within the network-level MTU.

See set_ciphertext_mtu for more details.

返回类型:

int

statistics()

Returns a DTLSChannelStatistics object with statistics about this connection.

返回类型:

DTLSChannelStatistics

await aclose()

Close this connection, but asynchronously.

This is included to satisfy the trio.abc.Channel contract. It's identical to close, but async.

返回类型:

None

close()

Close this connection.

DTLSChannels don't actually own any OS-level resources – the socket is owned by the DTLSEndpoint, not the individual connections. So you don't really have to call this. But it will interrupt any other tasks calling receive with a ClosedResourceError, and cause future attempts to use this connection to fail.

You can also use this object as a synchronous or asynchronous context manager.

返回类型:

None

await do_handshake(*, initial_retransmit_timeout=1.0)

Perform the handshake.

Calling this is optional – if you don't, then it will be automatically called the first time you call send or receive. But calling it explicitly can be useful in case you want to control the retransmit timeout, use a cancel scope to place an overall timeout on the handshake, or catch errors from the handshake specifically.

It's safe to call this multiple times, or call it simultaneously from multiple tasks – the first call will perform the handshake, and the rest will be no-ops.

参数:

initial_retransmit_timeout (float) --

Since UDP is an unreliable protocol, it's possible that some of the packets we send during the handshake will get lost. To handle this, DTLS uses a timer to automatically retransmit handshake packets that don't receive a response. This lets you set the timeout we use to detect packet loss. Ideally, it should be set to ~1.5 times the round-trip time to your peer, but 1 second is a reasonable default. There's some useful guidance here.

This is the initial timeout, because if packets keep being lost then Trio will automatically back off to longer values, to avoid overloading the network.

返回类型:

None

get_cleartext_mtu()

Returns the largest number of bytes that you can pass in a single call to send while still fitting within the network-level MTU.

See set_ciphertext_mtu for more details.

返回类型:

int

await receive()

Fetch the next packet of data from this connection's peer, waiting if necessary.

This is safe to call from multiple tasks simultaneously, in case you have some reason to do that. And more importantly, it's cancellation-safe, meaning that cancelling a call to receive will never cause a packet to be lost or corrupt the underlying connection.

返回类型:

bytes

await send(data)

Send a packet of data, securely.

返回类型:

None

set_ciphertext_mtu(new_mtu)

Tells Trio the largest amount of data that can be sent in a single packet to this peer.

Trio doesn't actually enforce this limit – if you pass a huge packet to send, then we'll dutifully encrypt it and attempt to send it. But calling this method does have two useful effects: :rtype: None

  • If called before the handshake is performed, then Trio will automatically fragment handshake messages to fit within the given MTU. It also might fragment them even smaller, if it detects signs of packet loss, so setting this should never be necessary to make a successful connection. But, the packet loss detection only happens after multiple timeouts have expired, so if you have reason to believe that a smaller MTU is required, then you can set this to skip those timeouts and establish the connection more quickly.

  • It changes the value returned from get_cleartext_mtu. So if you have some kind of estimate of the network-level MTU, then you can use this to figure out how much overhead DTLS will need for hashes/padding/etc., and how much space you have left for your application data.

The MTU here is measuring the largest UDP payload you think can be sent, the amount of encrypted data that can be handed to the operating system in a single call to send. It should not include IP/UDP headers. Note that OS estimates of the MTU often are link-layer MTUs, so you have to subtract off 28 bytes on IPv4 and 48 bytes on IPv6 to get the ciphertext MTU.

By default, Trio assumes an MTU of 1472 bytes on IPv4, and 1452 bytes on IPv6, which correspond to the common Ethernet MTU of 1500 bytes after accounting for IP/UDP overhead.

statistics()

Returns a DTLSChannelStatistics object with statistics about this connection.

返回类型:

DTLSChannelStatistics

class trio.DTLSChannelStatistics(incoming_packets_dropped_in_trio)

基类:object

Currently this has only one attribute:

  • incoming_packets_dropped_in_trio (int): Gives a count of the number of incoming packets from this peer that Trio successfully received from the network, but then got dropped because the internal channel buffer was full. If this is non-zero, then you might want to call receive more often, or use a larger incoming_packets_buffer, or just not worry about it because your UDP-based protocol should be able to handle the occasional lost packet, right?