在Ruby on Rails中获取主机名或IP

88

我正在维护一个Ruby on Rails应用程序,并正在寻找一种简单的方法来查找我所在机器的主机名或IP地址(因为它是一个虚拟机,新实例可能具有不同的主机名或IP地址)。在Ruby on Rails中有没有快速简便的方法来做到这一点?

编辑:下面的答案是正确的,但克雷格提供的澄清信息很有用(请参见答案中提供的链接):

以下代码不会建立连接或发送任何数据包(到谷歌的64.233.187.99地址)。由于UDP是无状态协议,connect()仅仅进行了一个系统调用,该调用基于地址和接口确定如何路由数据包(以及因此需要绑定哪个接口和IP地址)。addr()返回一个包含套接字族(AF_INET)、本地端口以及本地地址(我们想要的内容)的数组。

12个回答

148

主机名

Ruby中一个简单的方法只是获取主机名:

require 'socket'
hostname = Socket.gethostname
这依赖于主机知道自己的名称,因为它使用gethostnameuname系统调用,所以对于原始问题无效。功能上与hostname答案相同,而不需要调用外部程序。主机名可能是完全限定的,也可能不是,这取决于机器的配置。

IP地址

从Ruby 1.9开始,您还可以使用Socket库获取本地地址列表。ip_address_list返回一个AddrInfo对象数组。如何从中选择取决于您想要做什么和您有多少接口,但以下示例仅选择第一个非回送IPv4 IP地址作为字符串:
require 'socket'
ip_address = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address

Socket.gethostname 并不总是返回完整域名。如果需要完整域名,使用 'hostname'.strip 可能更加可靠。 - Travis Bear
Socket.gethostname 显然并不总是稳定的,参见评论 https://dev59.com/AWYq5IYBdhLWcg3w5Um8 - rogerdpack
官方文档页面:https://ruby-doc.org/stdlib/libdoc/socket/rdoc/Socket.html#method-c-gethostname - HarlemSquirrel
在 Ruby 2.7+ 中,Socket.gethostname 已被弃用。 - VP.

51

来自coderrr.wordpress.com

require 'socket'

def local_ip
  orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily

  UDPSocket.open do |s|
    s.connect '64.233.187.99', 1
    s.addr.last
  end
ensure
  Socket.do_not_reverse_lookup = orig
end

# irb:0> local_ip
# => "192.168.0.127"

1
根据上述问题的编辑,该IP地址属于Google公司。 - Topher Fangio

25

试试这个:

host = `hostname`.strip # Get the hostname from the shell and removing trailing \n
puts host               # Output the hostname

5
我喜欢使用Socket.gethostname函数因为它简洁纯粹,但在你确定"hostname"命令能够正常工作的系统中,你无法找到比它更简单的解决方案。 - Dave Smylie
你也可以执行 uname -n.sub(/..*/,'')。 - Tilo

13

一台服务器通常有多个接口,至少有一个私有和一个公有。

由于所有这里的答案都处理了这个简单的场景,更好的方法是从Socket中获取当前的ip_address_list()

require 'socket'

def my_first_private_ipv4
  Socket.ip_address_list.detect{|intf| intf.ipv4_private?}
end

def my_first_public_ipv4
  Socket.ip_address_list.detect{|intf| intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast? and !intf.ipv4_private?}
end

这两个函数都返回一个Addrinfo对象,所以如果你需要一个字符串,可以使用ip_address()方法,例如:

ip= my_first_public_ipv4.ip_address unless my_first_public_ipv4.nil?

通过更改Addrinfo方法来过滤所需接口地址,您可以轻松地找到更合适的解决方案。


10

在controller.rb中,最简单的方法是使用host_with_port方法。


host_port= request.host_with_port

2
缺点是您必须在控制器中才能访问请求...但是这是个好主意! - Tilo
似乎其他答案只是一些hack,而这个是正确的方法。为什么它被埋没在它们下面? - tbodt
这个具体告诉你用户是如何访问你的网站的 - 如果他们使用了IP地址,你就会得到IP地址。如果他们使用了localhost,你就会得到localhost。虽然这可能并不是没有用的,但不清楚这是否是原帖作者所期望的。 - undefined

9

这里使用的IP地址是谷歌的,但您可以使用任何可访问的IP地址。

require "socket"
local_ip = UDPSocket.open {|s| s.connect("64.233.187.99", 1); s.addr.last}

3

与使用 hostname 命令相似,在 UNIX/LINUX 上使用外部命令 uname

hostname = `uname -n`.chomp.sub(/\..*/,'')  # stripping off "\n" and the network name if present

对于正在使用的IP地址(您的计算机可能有多个网络接口),您可以使用以下方法:

 # on a Mac:
 ip_addresses = `ifconfig | grep 'inet ' | grep -v 127.0.0.1 | cut -d' ' -f 2`.split
 => ['10.2.21.122','10.8.122.12']

 # on Linux:
 ip_addresses = `ifconfig -a | grep 'inet ' | grep -v 127.0.0.1 | cut -d':' -f 2 | cut -d' ' -f 1`.split
 => ['10.2.21.122','10.8.122.12']

2
接受的答案可行,但每个请求都需要创建一个socket,如果服务器在本地网络上和/或未连接到互联网,则无法正常工作。我相信以下内容将始终有效,因为它解析了请求头。
request.env["SERVER_ADDR"]

3
OP并没有明确表示他们在需要IP地址时能够访问请求对象。基于Socket的解决方案更加通用。 - Chris McCauley

2
请用反引号将高亮部分括起来:
`dig #{request.host} +short`.strip # dig gives a newline at the end

如果您不关心是否为IP,只需使用request.host即可。


1
io = IO.popen('hostname')
hostname = io.readlines

io = IO.popen('ifconfig')
ifconfig = io.readlines
ip = ifconfig[11].scan(/\ \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\ /)

使用require 'socket'的答案看起来不错。使用request.blah_blah_blah的那些答案假设您正在使用Rails。

IO应该始终可用。这个脚本唯一的问题是,如果在您的系统上以不同的方式输出ifconfig,则IP的结果将不同。主机名查找应该像Sears一样稳定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接