在 Python 中查询现有套接字的地址族
Posted
技术标签:
【中文标题】在 Python 中查询现有套接字的地址族【英文标题】:Query an existing socket's address family in Python 【发布时间】:2018-08-11 09:18:52 【问题描述】:我使用 UNIX 域套接字将套接字文件描述符传递给 Python 3 程序。我想查询套接字并确定它的地址族。
在这个answer 中,getsockname
系统调用用于在 C 中实现相同的结果。但是,Python 的 socket.getsockname
函数不返回地址族,而是假设调用者已经知道它。此外,它仅适用于套接字,不适用于文件描述符,并且从文件描述符创建套接字对象也需要传递地址族。
有没有办法从 Python 中查询地址族?
编辑:我想强调我正在寻找一个已经打开的套接字的地址族,我有文件描述符。
【问题讨论】:
【参考方案1】:您只需将文件描述符传递给socket.socket
,将其作为fileno=
关键字属性。套接字类计算出与文件描述符对应的套接字类型。然后您可以按照@klaus 的建议找到家人:s.family
。
import socket
s = socket.socket()
print("original socket", s)
desc = s.fileno()
print("file descriptor", type(desc), desc)
new_s = socket.socket(fileno=desc)
print("new socket", new_s.family, int(new_s.family))
这里我在 python 中创建了一个套接字,然后从套接字对象中提取文件描述符(生成一个包含描述符编号的普通int
),以便演示将其放回一个新的套接字对象中。运行上述产生:
original socket <socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('0.0.0.0', 0)>
file descriptor <class 'int'> 3
new socket AddressFamily.AF_INET 2
编辑
嗯...上述方法似乎不适用于AF_INET6
套接字,至少在我的python 3.5.2版本中。
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
new_s = socket.socket(fileno=s.fileno())
print(new_s)
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('::%1', 0, 0, 1)>
这对我来说似乎是一个明显的错误。文档说:
如果指定了 fileno,则从指定的文件描述符中自动检测 family、type 和 proto 的值。
您可能需要检查new_s.laddr
并启发式地确定族。 (对于AF_INET6
,laddr
将是一个 4 元组,而 AF_INET
使用一个 2 元组)。如果您期望 AF_UNIX
或其他家庭,您可能需要扩展它。
【讨论】:
谢谢。我完全错过了文档的这一部分。我看到有一张关于这个的公开票:bugs.python.org/issue28134,尽管我检查了 Linux 上的 Python 3.7 并且它在那里工作。【参考方案2】:您可以在 python 中检查 .family 属性来查询套接字的地址族。 sock 对象中还保存了其他属性
import socket
import sys
# Create a TCP/IP socket
sock = socket.create_connection(('www.***.com', 80))
print(sock)
print(sock.family)
输出是
<socket.socket fd=3, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('x.x.x.x', y), raddr=('x.x.x.x', 80)>
AddressFamily.AF_INET
【讨论】:
谢谢,但是当我写 palivek 时,我想找到一个现有套接字的地址族,我有一个打开的文件描述符,而不是转换地址以找到选定的地址族。 【参考方案3】:如果您只想了解有关套接字的信息,我建议您使用socket.getaddrinfo
。
Documentation:
该函数返回具有以下结构的 5 元组列表:
(family, type, proto, canonname, sockaddr)
>>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET6: 10>, <SocketType.SOCK_STREAM: 1>,
6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)),
(<AddressFamily.AF_INET: 2>, <SocketType.SOCK_STREAM: 1>,
6, '', ('93.184.216.34', 80))]
【讨论】:
谢谢,但我想找到现有套接字的地址族,我有一个打开的文件描述符,而不是转换地址。【参考方案4】:这是一个基于我编写的 ctypes 的解决方案,因此我可以使用旧版本的 Python:
import os
import socket
from ctypes import *
libc = CDLL('libc.so.6', use_errno=True)
libc.getsockname.argtypes = [c_int, c_void_p, POINTER(c_int32)]
libc.getsockopt.argtypes = [c_int, c_int, c_int, c_void_p, POINTER(c_int32)]
def get_sock_family(fd):
family = c_ushort(0)
length = c_int32(sizeof(family))
ret = libc.getsockname(fd, byref(family), length)
if ret:
errno = get_errno()
raise OSError(errno, os.strerror(errno))
return socket.AddressFamily(family.value)
def get_sock_type(fd):
socket_type = c_int()
length = c_int32(sizeof(socket_type))
ret = libc.getsockopt(fd, socket.SOL_SOCKET, socket.SO_TYPE, byref(socket_type), length)
if ret:
errno = get_errno()
raise OSError(errno, os.strerror(errno))
return socket.SocketKind(socket_type.value)
【讨论】:
以上是关于在 Python 中查询现有套接字的地址族的主要内容,如果未能解决你的问题,请参考以下文章
C语言socket()函数解析(创建套接字)af地址族,ip地址类型(Address Family)INET(Inetnet)PF(Protocol Family)