IP Address 的 Integer 的表示方法
周五在 review Tim 的代码的时候,发现代码中有一个挺有趣的处理的地方。大致需求是这样的,一个 URL,需要统计它被独立的 IP 点击的次数。其中处理 IP 存储的地方比较特别,以 IP 106.187.97.192
为例,如果我们需要存储下来它的访问记录的话,可以直接存储字符串。但是这样会有两个不好的地方
- 存储占用的空间增加
- 查询的时候效率会低一些
而 Tim 采用的方法是将 IP 转换为一个 Big Integer
来存储,这样前面的两个问题都得到了解决,大致的过程是这样
require 'ipaddr'
ip_str = '106.187.97.192'
ip = IPAddr.new(str).to_i
#=> 1790665152
要取转回 Human readable IP
的时候,只需要再执行下面的方法,Socket::AF_INET
表示 IPV4
版本的套接字
IPAddr.new(ip, Socket::AF_INET).to_s
当时觉得这个方法很酷,可是并不理解这背后的原理是怎样的,为什么一个 IP Address 可以转换成这么一串数字,它们之间有什么样的联系呢。带着问题去 Google 了一下,发现原来是这样的
一个 IPV4 地址由 4 组数字组成,各自的范围在 0~255。每一组数字可以用 8 位二进制数字来表示,合计共需 32 位二进制
所以 IP Address 转成二进制之后为 01101010.10111011.01100001.11000000
那么最后的那个 Big Integer
1790665152 又是怎么来的呢,由于 IP Address 的 4 组数字每种数字都有 256 中可能(不确定这是不是所谓的 256 进制,还需要再确定一下,先称为 IP 进制吧),因此转换为 IP 进制的过程就是这样的
106 * 256^3 + 187 * 256^2 + 97 * 256^1 + 192 * 256^0
最后的结果就是 1790665152
,明白原理之后发现这其实是个很简单的问题,但是如果不知道 IP Address 背后的故事的话,再简单也是白搭。这也再次说明基础知识的主要性,也许学的时候觉得没什么用,但是再我们开发的时候也许就是他们给我们提供无限的灵感。