【问题标题】:Inexplicable node.js http throwing connect ECONNREFUSED (IPv6?)莫名其妙的node.js http抛出连接ECONNREFUSED(IPv6?)
【发布时间】:2013-03-05 15:06:55
【问题描述】:

我运行node.js如下:

> http = require('http')
> http.get('http://myhost.local:8080',
    function (res) { console.log("RES" + res) }
  ).on('error', function (e) { console.log("Error:", e) })

> uri = require('url').parse("http://myhost.local:8080")
{ protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'myhost.local:8080',
  port: '8080',
  hostname: 'myhost.local',
  hash: null,
  search: null,
  query: null,
  pathname: '/',
  path: '/',
  href: 'http://myhost.local:8080/' }
> http.get(uri,
    function (res) { console.log("RES" + res) }
  ).on('error', function (e) { console.log("Error:", e) })

隐式和显式解析的 URI 均引发错误,我得到以下输出:

错误:{ [错误:连接 ECONNREFUSED] 代码:'ECONNREFUSED', 错误号:'ECONNREFUSED', 系统调用:“连接”}

主机myhost.locallocalhost/etc/hosts中的别名,是:

127.0.0.1   localhost myhost.local myhost
255.255.255.255 broadcasthost
::1             localhost myhost.local myhost
fe80::1%lo0 localhost myhost.local myhost

编辑:我几乎尝试了主机文件的所有排列,包括最明显的:

127.0.0.1   localhost 
255.255.255.255 broadcasthost
::1             localhost myhost.local myhost
fe80::1%lo0 localhost

编辑我还应该提到,我现在已经在不止一台 Mac 上尝试过这个。

虽然这似乎是一个相当常见的错误,但我没有看到任何有用的解释或解决方法。以下是一些值得注意的相关事实:

  1. 运行 $ wget http://myhost.local:8080 可以正常工作,所以这不是防火墙问题。
  2. 运行 $ telnet myhost.local 8080 然后手动获取 url 工作正常,所以这不是一个奇怪的 HTTP 问题。
  3. 我使用 node.js 连接到其他主机没有问题,例如http://www.google.com

我希望有用的系统信息包括:

$ node -v
v0.9.11

$ uname -a
Darwin myhost.local 12.2.1 Darwin Kernel Version 12.2.1:
Thu Oct 18 12:13:47 PDT 2012; root:xnu-2050.20.9~1/RELEASE_X86_64 x86_64

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.8.2
BuildVersion:   12C3104

$ sudo  netstat -nalt | grep LISTEN | grep 8080
tcp6       0      0  ::1.8080  *.*    LISTEN

有没有人知道这里发生了什么,可能有什么修复方法?

【问题讨论】:

  • 如果你使用localhost而不是myhost.local,你会得到同样的错误吗?
  • 是否有东西在 localhost:80 上运行?
  • @JohnnyHK, @Floby: 连接的应用是Google App Engine,绑定到myhost。不过,让我发布我的netstat 输出 - 它可能很有启发性
  • 我在想 get 方法没有正确解析端口部分,而是连接到其他东西。试试下面的回答。
  • 根据您的netstat,您的 8080 服务绑定到 IPv6 本地主机,而不是 IPv4 本地主机。这可能是问题所在。

标签: node.js ipv6 node.js-connect


【解决方案1】:

我将在此处发布此内容,以防其他人遇到问题。

Bert Belder,Node.js 邮件列表:

在您的系统上,“myhost.local”解析为三个不同的地址 (127.0.0.1、::1 和 fe80::1)。 Node 更喜欢 ipv4 而不是 ipv6,所以它会 尝试连接到 127.0.0.1。 127.0.0.1:8080 上没有任何东西在听,所以 connect() 系统调用因 ECONNREFUSED 而失败。节点不重试 任何其他已解析的 IP - 它只会向您报告错误。一种 简单的解决方案是将 'localhost' 替换为预期的 目标 IP 地址,'::1'。

这种行为是否正确尚有待商榷,但这 是什么原因造成的。

伯特

【讨论】:

  • 感谢 alessioalex。很抱歉,我没有提到我尝试了/etc/hosts 文件的所有排列,包括只有::1 localhost myhost.local。无济于事。
  • 我应该注意到解决方案是将服务器绑定到IPv4,但这当然是错误的解决方案。 :)
  • “节点更喜欢 ipv4 而不是 ipv6”绝对是错误的行为。见RFC 6724
【解决方案2】:

这源于 Node 的一个问题(尽管有解决它的方法),根据discussion on nodejs/Google Groups,正如@alessioalex 在他的回答中提到的那样。 Bert Belder 的有用评论:

应该有一个 getaddrinfo 包装器,它返回的不仅仅是第一个结果

例如,

> require('dns').lookup('myhost.local', console.log)
{ oncomplete: [Function: onanswer] }
> null '127.0.0.1' 4

这是传递给 Node 的多个 getaddrinfo 结果中的第一个。似乎nodejs只使用了getaddrinfo调用的第一项。 Bert 和 Ben Noordhuis 在小组讨论中同意,应该有一种方法可以使用 getaddrinfo 包装器返回不仅仅是第一个结果。

对比python,返回来自getaddrinfo的所有结果:

>>> import socket
>>> socket.getaddrinfo("myhost.local", 8080)
[(30, 2, 17, '', ('::1', 8080, 0, 0)),
 (30, 1, 6, '',  ('::1', 8080, 0, 0)),
 (2, 2, 17, '',  ('127.0.0.1', 8080)),
 (2, 1, 6, '',   ('127.0.0.1', 8080)),
 (30, 2, 17, '', ('fe80::1%lo0', 8080, 0, 1)),
 (30, 1, 6, '',  ('fe80::1%lo0', 8080, 0, 1))]

【讨论】:

    【解决方案3】:

    这行得通吗?

    var http = require('http');
    var options = {
      host: 'myhost.local',
      port: 8080,
      path: '/'
    };
    
    http.get(options, function (res) { 
      console.log("RES" + res) 
    }).on('error', function (e) { 
      console.log("Error:", e) 
    });
    

    【讨论】:

    • 谢谢曼努埃尔。这实际上与require('url').parse("http://myhost.local:8080/") 相同,这是http.get 在传递字符串时调用的内容。为了清楚起见,我更新了我的问题以反映这一点。
    猜你喜欢
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2013-02-14
    • 1970-01-01
    相关资源
    最近更新 更多