【问题标题】:Force python mechanize/urllib2 to only use A requests?强制 python mechanize/urllib2 只使用 A 请求?
【发布时间】:2010-01-06 16:43:31
【问题描述】:

这是一个相关问题,但我不知道如何将答案应用于 mechanize/urllib2:how to force python httplib library to use only A requests

基本上,给出这个简单的代码:

#!/usr/bin/python
import urllib2
print urllib2.urlopen('http://python.org/').read(100)

这导致wireshark说以下内容:

  0.000000  10.102.0.79 -> 8.8.8.8      DNS Standard query A python.org
  0.000023  10.102.0.79 -> 8.8.8.8      DNS Standard query AAAA python.org
  0.005369      8.8.8.8 -> 10.102.0.79  DNS Standard query response A 82.94.164.162
  5.004494  10.102.0.79 -> 8.8.8.8      DNS Standard query A python.org
  5.010540      8.8.8.8 -> 10.102.0.79  DNS Standard query response A 82.94.164.162
  5.010599  10.102.0.79 -> 8.8.8.8      DNS Standard query AAAA python.org
  5.015832      8.8.8.8 -> 10.102.0.79  DNS Standard query response AAAA 2001:888:2000:d::a2

这是 5 秒延迟

我的系统中的任何地方都没有启用 IPv6(gentoo 使用 USE=-ipv6 编译)所以我认为 python 没有任何理由尝试 IPv6 查找。

上面引用的问题建议将套接字类型明确设置为AF_INET,这听起来很棒。我不知道如何强制 urllib 或 mechanize 使用我创建的任何套接字。

编辑:我知道 AAAA 查询是问题所在,因为其他应用程序也有延迟,一旦我在禁用 ipv6 的情况下重新编译,问题就消失了……除了在 python 中它仍然执行 AAAA 请求。

【问题讨论】:

  • 这里相同,在连接到不同供应商的不同机器上。我使用了 libwww-perl,它是 GET 命令——它可以立即在所有机器上运行。

标签: python mechanize ipv6 urllib


【解决方案1】:

遇到同样的问题,根据 J.J. .

这基本上将socket.getaddrinfo(..)family 参数强制为socket.AF_INET,而不是使用socket.AF_UNSPEC(零,这​​似乎在socket.create_connection 中使用),不仅适用于来自urllib2 的调用,而且应该为所有对socket.getaddrinfo(..) 的调用执行此操作:

#--------------------
# do this once at program startup
#--------------------
import socket
origGetAddrInfo = socket.getaddrinfo

def getAddrInfoWrapper(host, port, family=0, socktype=0, proto=0, flags=0):
    return origGetAddrInfo(host, port, socket.AF_INET, socktype, proto, flags)

# replace the original socket.getaddrinfo by our version
socket.getaddrinfo = getAddrInfoWrapper

#--------------------
import urllib2

print urllib2.urlopen("http://python.org/").read(100)

至少在这个简单的情况下这对我有用。

【讨论】:

  • 刚刚测试过,在 python 3.5.2 中仍然可以完美运行。
【解决方案2】:

没有答案,但有几个数据点。 DNS 解析似乎源自HTTPConnection.connect() 中的httplib.py(我的python 2.5.4 stdlib 上的第670 行)

代码流程大致是:

for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    self.sock = socket.socket(af, socktype, proto)
    try:
        self.sock.connect(sa)
    except socket.error, msg: 
        continue
    break

关于正在发生的事情的几个cmet:

  • socket.getaddrinfo() 的第三个参数限制了套接字系列——即 IPv4 与 IPv6。通过零返回所有家庭。零被硬编码到标准库中。

  • 将主机名传递给 getaddrinfo() 将导致名称解析 - 在我的启用 IPv6 的 OS X 机器上,A 和 AAAA 记录都出去了,两个答案都会立即返回并返回。

  • 连接循环的其余部分尝试每个返回的地址,直到一个成功

例如:

>>> socket.getaddrinfo("python.org", 80, 0, socket.SOCK_STREAM)
[
 (30, 1, 6, '', ('2001:888:2000:d::a2', 80, 0, 0)), 
 ( 2, 1, 6, '', ('82.94.164.162', 80))
]
>>> help(socket.getaddrinfo)
getaddrinfo(...)
    getaddrinfo(host, port [, family, socktype, proto, flags])
        -> list of (family, socktype, proto, canonname, sockaddr)

一些猜测:

  • 由于getaddrinfo() 中的套接字系列被硬编码为零,您将无法通过 urllib 中某些受支持的 API 接口覆盖 A 与 AAAA 记录。除非机械化出于其他原因进行自己的名称解析,否则机械化也不能。从连接循环的构造来看,这是设计使然。

  • python 的套接字模块是 POSIX 套接字 API 的薄包装器;我期望他们正在解决系统上可用和配置的每个家庭。仔细检查 Gentoo 的 IPv6 配置。

【讨论】:

  • 在我看来,python 不应该将0 传递给socket.getaddrinfo,如果它是在不支持 ipv6 的情况下构建的。也许这在某些方面可以被认为是一个小错误。
【解决方案3】:

当被问及 python.org 的 AAAA 时,DNS 服务器 8.8.8.8 (Google DNS) 会立即回复。因此,我们在您发布的跟踪中没有看到此回复的事实可能表明此数据包没有返回(UDP 发生这种情况)。如果这个损失是随机的,那是正常的。如果是系统性的,则表示您的网络设置存在问题,可能是防火墙损坏,导致第一个 AAAA 回复无法返回。

5 秒延迟来自您的存根解析器。这种情况下,如果是随机的,可能是运气不好,但与 IPv6 无关,对 A 记录的回复也可能失败。

禁用 IPv6 似乎是一个非常奇怪的举动,距离最后一个 IPv4 地址分发只有两年时间!

% dig @8.8.8.8  AAAA python.org

; <<>> DiG 9.5.1-P3 <<>> @8.8.8.8 AAAA python.org
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50323
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;python.org.                    IN      AAAA

;; ANSWER SECTION:
python.org.             69917   IN      AAAA    2001:888:2000:d::a2

;; Query time: 36 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sat Jan  9 21:51:14 2010
;; MSG SIZE  rcvd: 67

【讨论】:

  • 好吧,我很乐意使用 IPv6...一旦它停止为我的 DNS 查询添加 5 秒延迟:-P。不幸的是,这并不是“运气不好”,而是每一个查询。
【解决方案4】:

最可能的原因是broken egress firewall。例如,瞻博网络防火墙可能会导致这种情况,尽管它们有一个可用的workaround

如果您无法让网络管理员修复防火墙,您可以尝试基于主机的解决方法。将此行添加到您的 /etc/resolv.conf:

options single-request-reopen

手册页解释得很好:

解析器对 A 和 AAAA 请求使用相同的套接字。一些硬件错误地只发回一个回复。当这种情况发生时,客户端系统将坐下来等待第二个回复。打开此选项会更改此行为,因此如果来自同一端口的两个请求未正确处理,它将关闭套接字并在发送第二个请求之前打开一个新的。

【讨论】:

  • 感谢这解决了我在 python 中遇到的 ipv6 名称解析段错误问题。
猜你喜欢
  • 1970-01-01
  • 2016-03-08
  • 1970-01-01
  • 2015-07-14
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-07
相关资源
最近更新 更多