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 背后的故事的话,再简单也是白搭。这也再次说明基础知识的主要性,也许学的时候觉得没什么用,但是再我们开发的时候也许就是他们给我们提供无限的灵感。