使用 trio.socket
进行低级网络编程¶
Low-level networking with :mod:`trio.socket`
trio.socket
模块提供了 Trio 的基本底层网络 API。如果您正在通过 IPv4/IPv6/Unix 域套接字进行普通的流式连接操作, 那么您可能希望使用上面描述的高层 API。如果您想使用 UDP, 或使用像 AF_BLUETOOTH
这样的特殊地址族, 或者需要直接访问系统网络 API 的所有奇怪部分, 那么您来对地方了。
The trio.socket
module provides Trio's basic low-level
networking API. If you're doing ordinary things with stream-oriented
connections over IPv4/IPv6/Unix domain sockets, then you probably want
to stick to the high-level API described above. If you want to use
UDP, or exotic address families like AF_BLUETOOTH
, or otherwise
get direct access to all the quirky bits of your system's networking
API, then you're in the right place.
顶级导出¶
Top-level exports
通常, trio.socket
模块暴露的 API 与标准库的 socket
模块相似。大多数常量 (如 SOL_SOCKET
) 和简单的工具函数 (如 inet_aton()
) 都只是重新导出了, 保持不变。但也有一些不同之处, 下面将进行描述。
首先, Trio 提供了所有返回套接字对象的标准库函数的类比;它们的接口完全相同, 只是它们被修改为返回 Trio 套接字对象:
- trio.socket.socket(family=-1, type=-1, proto=-1, fileno=None)¶
Create a new Trio socket, like
socket.socket
.This function's behavior can be customized using
set_custom_socket_factory()
.
- trio.socket.socketpair(family=None, type=SocketKind.SOCK_STREAM, proto=0)¶
Like
socket.socketpair()
, but returns a pair of Trio socket objects.
- trio.socket.fromfd(fd, family, type, proto=0)¶
Like
socket.fromfd()
, but returns a Trio socket object.
类似于
socket.fromshare()
, 但返回一个 Trio 套接字对象。
此外, 还有一个新函数可以直接将标准库的套接字转换为 Trio 套接字:
- trio.socket.from_stdlib_socket(sock)¶
Convert a standard library
socket.socket
object into a Trio socket object.- 返回类型:
与 socket.socket
不同, trio.socket.socket()
是一个函数, 而不是一个类;如果您想检查一个对象是否是 Trio 套接字, 请使用 isinstance(obj, trio.socket.SocketType)
。
对于名称查找, Trio 提供了标准的函数, 但做了一些修改:
- await trio.socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)¶
Look up a numeric address given a name.
Arguments and return values are identical to
socket.getaddrinfo()
, except that this version is async.Also,
trio.socket.getaddrinfo()
correctly uses IDNA 2008 to process non-ASCII domain names. (socket.getaddrinfo()
uses IDNA 2003, which can give the wrong result in some cases and cause you to connect to a different host than the one you intended; see bpo-17305.)This function's behavior can be customized using
set_custom_hostname_resolver()
.
- await trio.socket.getnameinfo(sockaddr, flags)¶
Look up a name given a numeric address.
Arguments and return values are identical to
socket.getnameinfo()
, except that this version is async.This function's behavior can be customized using
set_custom_hostname_resolver()
.
- await trio.socket.getprotobyname(name)¶
Look up a protocol number by name. (Rarely used.)
Like
socket.getprotobyname()
, but async.- 返回类型:
Trio 特意不包含一些过时的, 冗余的或损坏的功能:
gethostbyname()
,gethostbyname_ex()
,gethostbyaddr()
:已过时;请改用getaddrinfo()
和getnameinfo()
。getservbyport()
:已过时且存在 bug;请改用:
_, service_name = await getnameinfo(('127.0.0.1', port), NI_NUMERICHOST)
getservbyname()
:已过时且存在 bug;请改用:
await getaddrinfo(None, service_name)
getfqdn()
:已过时;请使用getaddrinfo()
并带上AI_CANONNAME
标志。getdefaulttimeout()
,setdefaulttimeout()
:请使用 Trio 的标准支持来进行 取消 。在 Windows 上,
SO_REUSEADDR
没有被导出, 因为它是一个陷阱:这个名字与 Unix 上的SO_REUSEADDR
相同, 但语义是 不同且极为破坏性的 。在非常罕见的情况下, 如果您确实需要在 Windows 上使用SO_REUSEADDR
, 它仍然可以通过标准库的socket
模块访问。
Generally, the API exposed by trio.socket
mirrors that of the
standard library socket
module. Most constants (like
SOL_SOCKET
) and simple utilities (like inet_aton()
)
are simply re-exported unchanged. But there are also some differences,
which are described here.
First, Trio provides analogues to all the standard library functions that return socket objects; their interface is identical, except that they're modified to return Trio socket objects instead:
- trio.socket.socket(family=-1, type=-1, proto=-1, fileno=None)
Create a new Trio socket, like
socket.socket
.This function's behavior can be customized using
set_custom_socket_factory()
.
- trio.socket.socketpair(family=None, type=SocketKind.SOCK_STREAM, proto=0)
Like
socket.socketpair()
, but returns a pair of Trio socket objects.
- trio.socket.fromfd(fd, family, type, proto=0)
Like
socket.fromfd()
, but returns a Trio socket object.
- trio.socket.fromshare(data)
Like
socket.fromshare()
, but returns a Trio socket object.
In addition, there is a new function to directly convert a standard library socket into a Trio socket:
- trio.socket.from_stdlib_socket(sock)
Convert a standard library
socket.socket
object into a Trio socket object.- 返回类型:
Unlike socket.socket
, trio.socket.socket()
is a
function, not a class; if you want to check whether an object is a
Trio socket, use isinstance(obj, trio.socket.SocketType)
.
For name lookup, Trio provides the standard functions, but with some changes:
- await trio.socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
Look up a numeric address given a name.
Arguments and return values are identical to
socket.getaddrinfo()
, except that this version is async.Also,
trio.socket.getaddrinfo()
correctly uses IDNA 2008 to process non-ASCII domain names. (socket.getaddrinfo()
uses IDNA 2003, which can give the wrong result in some cases and cause you to connect to a different host than the one you intended; see bpo-17305.)This function's behavior can be customized using
set_custom_hostname_resolver()
.
- await trio.socket.getnameinfo(sockaddr, flags)
Look up a name given a numeric address.
Arguments and return values are identical to
socket.getnameinfo()
, except that this version is async.This function's behavior can be customized using
set_custom_hostname_resolver()
.
- await trio.socket.getprotobyname(name)
Look up a protocol number by name. (Rarely used.)
Like
socket.getprotobyname()
, but async.- 返回类型:
Trio intentionally DOES NOT include some obsolete, redundant, or broken features:
gethostbyname()
,gethostbyname_ex()
,gethostbyaddr()
: obsolete; usegetaddrinfo()
andgetnameinfo()
instead.getservbyport()
: obsolete and buggy; instead, do:
_, service_name = await getnameinfo(('127.0.0.1', port), NI_NUMERICHOST)
getservbyname()
: obsolete and buggy ; instead, do:
await getaddrinfo(None, service_name)
getfqdn()
: obsolete; usegetaddrinfo()
with theAI_CANONNAME
flag.getdefaulttimeout()
,setdefaulttimeout()
: instead, use Trio's standard support for 取消和超时.On Windows,
SO_REUSEADDR
is not exported, because it's a trap: the name is the same as UnixSO_REUSEADDR
, but the semantics are different and extremely broken. In the very rare cases where you actually wantSO_REUSEADDR
on Windows, then it can still be accessed from the standard library'ssocket
module.
套接字对象¶
Socket objects
- class trio.socket.SocketType¶
备注
trio.socket.SocketType
是一个抽象类, 不能直接实例化;您可以通过调用构造函数, 如trio.socket.socket()
, 来获得具体的套接字对象。然而, 您可以使用它来检查一个对象是否是 Trio 套接字, 方法是isinstance(obj, trio.socket.SocketType)
。Trio 套接字对象总体上与 标准库套接字对象 非常相似, 但有一些重要的区别:
首先, 也是最显著的, 所有内容都被改造成了“Trio 风格”: 阻塞方法变为异步方法, 并且以下属性 不 支持:
setblocking()
:Trio 套接字始终表现得像阻塞套接字;如果您需要同时从多个套接字读取/写入, 应该创建多个任务。settimeout()
:请改用 取消。makefile()
:Python 的类文件 API 是同步的, 因此无法在异步套接字上实现。sendall()
:可能支持, 但您最好使用更高层次的SocketStream
, 特别是它的send_all()
方法, 这个方法还会执行额外的错误检查。
此外, 以下方法与
socket.socket
中的相似, 但有一些 Trio 特有的怪癖:- await connect()¶
将套接字连接到远程地址。
类似于
socket.socket.connect()
, 但它是异步的。
- is_readable()¶
检查套接字是否可读。
我们还跟踪一个额外的状态位, 因为它对
trio.SocketStream
很有用:- did_shutdown_SHUT_WR¶
如果您调用了
sock.shutdown(SHUT_WR)
或sock.shutdown(SHUT_RDWR)
, 则此bool
属性为 True, 否则为 False。
以下方法与
socket.socket
中的对应方法相同, 唯一不同的是它们是异步的, 并且那些接受地址参数的方法需要预先解析的地址:recvmsg()
(如果可用 )recvmsg_into()
(如果可用 )sendmsg()
(如果可用 )
所有未在上面提到的方法和属性都与
socket.socket
中的对应项相同:
- class trio.socket.SocketType
备注
trio.socket.SocketType
is an abstract class and cannot be instantiated directly; you get concrete socket objects by calling constructors liketrio.socket.socket()
. However, you can use it to check if an object is a Trio socket viaisinstance(obj, trio.socket.SocketType)
.Trio socket objects are overall very similar to the standard library socket objects, with a few important differences:
First, and most obviously, everything is made "Trio-style": blocking methods become async methods, and the following attributes are not supported:
setblocking()
: Trio sockets always act like blocking sockets; if you need to read/write from multiple sockets at once, then create multiple tasks.settimeout()
: see 取消和超时 instead.makefile()
: Python's file-like API is synchronous, so it can't be implemented on top of an async socket.sendall()
: Could be supported, but you're better off using the higher-levelSocketStream
, and specifically itssend_all()
method, which also does additional error checking.
In addition, the following methods are similar to the equivalents in
socket.socket
, but have some Trio-specific quirks:- await connect()
Connect the socket to a remote address.
Similar to
socket.socket.connect()
, except async.警告
Due to limitations of the underlying operating system APIs, it is not always possible to properly cancel a connection attempt once it has begun. If
connect()
is cancelled, and is unable to abort the connection attempt, then it will:forcibly close the socket to prevent accidental reuse
raise
Cancelled
.
tl;dr: if
connect()
is cancelled then the socket is left in an unknown state – possibly open, and possibly closed. The only reasonable thing to do is to close it.
- is_readable()
Check whether the socket is readable or not.
- sendfile()
We also keep track of an extra bit of state, because it turns out to be useful for
trio.SocketStream
:- did_shutdown_SHUT_WR
This
bool
attribute is True if you've calledsock.shutdown(SHUT_WR)
orsock.shutdown(SHUT_RDWR)
, and False otherwise.
The following methods are identical to their equivalents in
socket.socket
, except async, and the ones that take address arguments require pre-resolved addresses:recvmsg()
(if available)recvmsg_into()
(if available)sendmsg()
(if available)
All methods and attributes not mentioned above are identical to their equivalents in
socket.socket
: