使用 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.socketobject 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.socketobject 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_CANONNAMEflag.getdefaulttimeout(),setdefaulttimeout(): instead, use Trio's standard support for 取消和超时.On Windows,
SO_REUSEADDRis 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_REUSEADDRon Windows, then it can still be accessed from the standard library'ssocketmodule.
套接字对象¶
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.SocketTypeis 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
boolattribute 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: