极低级套接字操作

简介

除了支持数据流(SOCK_STREAM)或数据报(SOCK_DGRAM)之外,POSIX 套接字还具有通过 send(2) 和 recv(2) 无法访问的其他功能。这些功能包括散射/收集 I/O、将文件描述符复制到其他进程以及访问带外数据。

Twisted 包含一个围绕两个 C API 的包装器,这些 API 使这些功能成为可能,sendmsgrecvmsg 。本文档介绍了它们的用法。它适用于 Twisted 维护人员。寻找此功能的应用程序开发人员应查看 Twisted 在这些包装器之上提供的更高级别 API。

sendmsg

sendmsg(2) 公开了套接字的几乎所有发送端功能。对于 SOCK_STREAM 套接字,它可以发送成为连接上传输的数据流的一部分的字节。对于 SOCK_DGRAM 套接字,它可以发送成为从套接字发送的数据报的字节。它可以从多个内存位置发送数据(收集 I/O)。通过 AF_UNIX 套接字,它可以将文件描述符复制到接收另一端套接字的任何进程中。Twisted 中包含的包装器,sendmsg,公开了许多(但不是全部)这些功能。本文档介绍了它所公开的功能的用法。此包装器的主要限制是接口一次只能发送一个 *iovec*。

recvmsg

同样,recvmsg(2) 公开了套接字的几乎所有接收端功能。它可以从 SOCK_STREAM 套接字接收流数据或从 SOCK_DGRAM 套接字接收数据报。它可以将这些数据接收进多个内存位置(散射 I/O),并且它可以接收这些复制的文件描述符。Twisted 中包含的包装器,recvmsg,公开了许多(但不是全部)这些功能。本文档介绍了它所公开的功能的用法。此包装器的主要限制是接口一次只能接收一个 *iovec*。

发送和接收常规数据

sendmsg 可以以使其等效于使用 send 调用的方式使用。sendmsg 的第一个参数是(在本例中和所有其他情况下)要通过其发送数据的套接字。第二个参数是一个字节字符串,用于指定要发送的数据。

在另一端,recvmsg 可以用来替换 recv 调用。recvmsg 的第一个参数是(同样,在所有情况下)要通过其接收数据的套接字。第二个参数是一个整数,用于指定要接收的最大字节数。

send_replacement.py

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
Demonstration of sending bytes over a TCP connection using sendmsg.
"""


from socket import socketpair

from twisted.python.sendmsg import recvmsg, sendmsg


def main():
    foo, bar = socketpair()
    sent = sendmsg(foo, b"Hello, world")
    print("Sent", sent, "bytes")
    (received, ancillary, flags) = recvmsg(bar, 1024)
    print("Received", repr(received))
    print("Extra stuff, boring in this case", flags, ancillary)


if __name__ == "__main__":
    main()

复制文件描述符

与 AF_UNIX 套接字一起使用时,sendmsg 会将文件描述符的副本发送到接收另一端套接字的任何进程中。这是使用辅助数据参数完成的。辅助数据由三个元组列表组成。使用 SOL_SOCKET、SCM_RIGHTS 和平台字节序打包的文件描述符号构造的三个元组将复制该文件描述符。

以这种方式复制的文件描述符必须使用 recvmsg 调用接收。接收这些描述符不需要任何特殊参数。它们将以本机顺序字符串的形式出现在 recvmsg 返回的辅助数据列表中。

copy_descriptor.py

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
Demonstration of copying a file descriptor over an AF_UNIX connection using
sendmsg.
"""


from os import pipe, read, write
from socket import SOL_SOCKET, socketpair
from struct import pack, unpack

from twisted.python.sendmsg import SCM_RIGHTS, recvmsg, sendmsg


def main():
    foo, bar = socketpair()
    reader, writer = pipe()

    # Send a copy of the descriptor.  Notice that there must be at least one
    # byte of normal data passed in.
    sent = sendmsg(foo, b"\x00", [(SOL_SOCKET, SCM_RIGHTS, pack("i", reader))])

    # Receive the copy, including that one byte of normal data.
    data, ancillary, flags = recvmsg(bar, 1024)
    duplicate = unpack("i", ancillary[0][2])[0]

    # Demonstrate that the copy works just like the original
    write(writer, b"Hello, world")
    print("Read from original (%d): %r" % (reader, read(reader, 6)))
    print("Read from duplicate (%d): %r" % (duplicate, read(duplicate, 6)))


if __name__ == "__main__":
    main()