10

经过数小时尝试调试遇到 fopen() 问题的第三方应用程序后,我终于发现

php -r 'echo(file_get_contents("http://www.google.com/robots.txt"));'

失败,但是

php -r 'echo(file_get_contents("http://173.194.32.81/robots.txt"));'

成功。请注意,作为网络服务器用户,我可以 ping www.google.com 并且解决得很好。

我跟踪了 PHP 的两个执行,它们的分歧是这样的:

对于数字 v4 URL:

socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("173.194
poll([{fd=3, events=POLLOUT}], 1, 0)    = 0 (Timeout)
...[bunch of poll/select/recvfrom]...
close(3)                                = 0

对于域名:

socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
close(3)                                 = 0

似乎 PHP 甚至没有尝试对那个套接字做任何事情。或者甚至解决域,就此而言。怎么回事?

在有或没有 ipv6 支持的情况下重新编译 PHP 似乎并不重要。在这个系统上禁用 ipv6 是不可取的。

Gentoo Linux,PHP 5.3.14,目前正在尝试 PHP 5.4,看看是否有帮助。有人有想法吗?

编辑:

php -r 'echo gethostbyname("www.google.com");'

工作并产生一个 ipv4,而

php -r 'echo(file_get_contents("http://[2a00:1450:4007:803::1011]/"));'

似乎返回一个空白结果。

编辑2:

第一次我什至没有注意到,使用名称时打开的 v6 套接字是 SOCK_DGRAM。这是 PHP 试图解析域名吗?我尝试在 resolv.conf 中将解析器从 127.0.0.1 切换到 ::1,但没有帮助。

4

2 回答 2

1

GDB 显示那个神秘的未使用的套接字调用实际上来自 libcurl。我在没有 libcurl 的情况下重新编译了 php,它可以工作。我会继续调查原因,但到目前为止,解决方法似乎有效。

于 2012-07-01T22:11:08.597 回答
0

我认为这是操作系统偏好,而不是 php 偏好。我认为您需要编辑您的 /etc/gai.conf 文件以选择 IPv4 而不是 IPv6。一点点谷歌搜索出现了这篇描述如何做到这一点的文章。老实说,我对 gai.conf 不熟悉,所以你的里程可能会有所不同,但看起来在大多数系统上它只是简单地取消注释一行。

至于为什么你看到 SOCK_DGRAM,如果我不得不猜测,我猜这是因为 DNS 查找是 UDP,这可能是你在跟踪中看到的,而在你的第一个跟踪中,地址可能被缓存了所以它立即建立到远程服务器的 TCP 连接(DOCK_STREAM)。

于 2012-07-01T21:14:00.357 回答