什么是外网IP和内网IP?

559 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

引言

上篇文章我们介绍了 面试题中IP地址常用操作以及Go中的net包,现在我们来梳理一下关于IP的一些概念,什么是外网IP和内网IP?最后写一个例子来测试一下你是不是也和我一样掌握了这些操作呢?

什么是外网IP和内网IP?

tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下: 
10.0.0.0/8:10.0.0.0~10.255.255.255 
172.16.0.0/12:172.16.0.0~172.31.255.255 
192.168.0.0/16:192.168.0.0~192.168.255.255

什么是内网IP

一些小型企业或者学校,通常都是申请一个固定的IP地址,然后通过IP共享(IP Sharing),使用整个公司或学校的机器都能够访问互联网。而这些企业或学校的机器使用的IP地址就是内网IP,内网IP是在规划IPv4协议时,考虑到IP地址资源可能不足,就专门为内部网设计私有IP地址(或称之为保留地址),一般常用内网IP地址都是这种形式的:10.X.X.X、172.16.X.X-172.31.X.X、192.168.X.X等。需要注意的是,内网的计算机可向Internet上的其他计算机发送连接请求,但Internet上其他的计算机无法向内网的计算机发送连接请求。我们平时可能在内网机器上搭建过网站或者FTP服务器,而在外网是不能访问该网站和FTP服务器的,原因就在于此。

什么是公网IP

公网IP就是除了保留IP地址以外的IP地址,可以与Internet上的其他计算机随意互相访问。我们通常所说的IP地址,其实就是指的公网IP。互联网上的每台计算机都有一个独立的IP地址,该IP地址唯一确定互联网上的一台计算机。这里的IP地址就是指的公网IP地址。

怎样理解互联网上的每台计算机都有一个唯一的IP地址

其实,互联网上的计算机是通过“公网IP+内网IP”来唯一确定的,就像很多大楼都是201房间一样,房间号可能一样,但是大楼肯定是唯一的。公网IP地址和内网IP地址也是同样,不同企业或学校的机器可能有相同的内网IP地址,但是他们的公网IP地址肯定不同。那么这些企业或学校的计算机是怎样IP地址共享的呢?这就需要使用NAT(Network Address Translation,网络地址转换)功能。当内部计算机要连接互联网时,首先需要通过NAT技术,将内部计算机数据包中有关IP地址的设置都设成NAT主机的公共IP地址,然后再传送到Internet,虽然内部计算机使用的是私有IP地址,但在连接Internet时,就可以通过NAT主机的NAT技术,将内网IP地址修改为公网IP地址,如此一来,内网计算机就可以向Internet请求数据了。

关于IP操作的例子

  1. 首先,通过dns查询本机的IP,判断他是公网IP还是内网IP
  2. 其次,通过访问 myexternalip.com/raw 获取公网ip
  3. 然后,测试IP地址(net.IP)与Int互转
  4. 最后,测试一个IP地址是否再给定的地址范围内,可以用来判断是否是内网地址
/*
* @Author: starine
* @Date:   2022/5/3 13:12
 */
package main

import (
   "bytes"
   "fmt"
   "io/ioutil"
   "net"
   "net/http"
   "os"
   "strconv"
   "strings"
)

func main() {
   fmt.Println("------通过dns查询本机的IP------")
   myIP := GetLocalIP()
   fmt.Println("通过dns查询本机的IP是:",myIP)
   is_public_ip := IsPublicIP(net.ParseIP(myIP))
   fmt.Println("本机的IP是不是公网IP: ", is_public_ip)
   fmt.Println()

   fmt.Println("------通过访问http://myexternalip.com/raw获取公网ip------")
   myPublicIP := GetMyPublicIP()
   fmt.Println("查询到本机的公网ip是: ", myPublicIP)
   is_public_ip2 := IsPublicIP(net.ParseIP(myPublicIP))
   fmt.Println("查询到本机的公网ip是不是公网IP: ", is_public_ip2)
   fmt.Println()

   fmt.Println("------IP地址与Int互转------")
   ip2int := netIPToInt(net.ParseIP(myPublicIP))
   fmt.Println("将IP地址转换成十进制:", ip2int)
   int2ip := intToNetIP(ip2int)
   fmt.Println("再将十进制转换成IP地址:", int2ip)
   fmt.Println()

   fmt.Println("------测试一个地址是否再给定的地址范围内------")
   is_between := IpBetween(net.ParseIP("192.168.0.0"), net.ParseIP("192.168.255.255"), net.ParseIP(myIP))
   fmt.Println("本地地址是否在 192.168.0.0 ~ 192.168.255.255之间(内网地址区间):", is_between)
}

// GetLocalIP 通过dns服务器8.8.8.8:80获取本地使用的ip
func GetLocalIP() string {
   // Dial() 在网络network上连接地址address,并返回一个Conn接口。
   conn, _ := net.Dial("udp", "8.8.8.8:80")
   defer conn.Close()
   localAddr := conn.LocalAddr().String()
   idx := strings.LastIndex(localAddr, ":")
   return localAddr[0:idx]
}

// GetMyPublicIP 通过访问http://myexternalip.com/raw获取公网ip
func GetMyPublicIP() string {
   resp, err := http.Get("http://myexternalip.com/raw")
   if err != nil {
      return ""
   }
   defer resp.Body.Close()
   content, _ := ioutil.ReadAll(resp.Body)
   return string(content)
}

// GetIntranetIp 获取本地IP,返回网络接口地址
func GetIntranetIp() {
   //InterfaceAddrs返回该系统的网络接口的地址列表。
   addrs, err := net.InterfaceAddrs()
   if err != nil {
      fmt.Println(err)
      os.Exit(1)
   }

   for _, address := range addrs {
      // 检查ip地址判断是否回环地址
      // IsLoopback() 如果ip是环回地址,则返回真。
      if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
         if ipnet.IP.To4() != nil {
            fmt.Println("ip:", ipnet.IP.String())
         }
      }
   }
}

// IsPublicIP 判断是否是公网ip
func IsPublicIP(IP net.IP) bool {
   //如果ip是环回地址、链路本地组播地址、链路本地单播地址 返回false
   if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
      return false
   }

   //如果是内网ip返回false
   //tcp/ip协议中,专门保留了三个IP地址区域作为私有地址,其地址范围如下:
   //10.0.0.0/8:     10.0.0.0 ~ 10.255.255.255
   //172.16.0.0/12:   172.16.0.0 ~ 172.31.255.255
   //192.168.0.0/16:  192.168.0.0 ~ 192.168.255.255
   if ip4 := IP.To4(); ip4 != nil {
      switch true {
      case ip4[0] == 10:
         return false
      case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
         return false
      case ip4[0] == 192 && ip4[1] == 168:
         return false
      default:
         return true
      }
   }
   return false //ip4 == nil
}

//将 int 转换成 netIP ([]byte)
func intToNetIP(ipnr int) net.IP {
   var bytes [4]byte
   bytes[0] = byte(ipnr >> 24)
   bytes[1] = byte(ipnr >> 16)
   bytes[2] = byte(ipnr >> 8)
   bytes[3] = byte(ipnr)

   return net.IPv4(bytes[0], bytes[1], bytes[2], bytes[3])
}

//将 netIP ([]byte) 转换成 uint32
func netIPToInt(ipnr net.IP) int {
   bits := strings.Split(ipnr.String(), ".")
   b0, _ := strconv.Atoi(bits[0])
   b1, _ := strconv.Atoi(bits[1])
   b2, _ := strconv.Atoi(bits[2])
   b3, _ := strconv.Atoi(bits[3])

   var sum int
   sum |= int(b0) << 24
   sum |= int(b1) << 16
   sum |= int(b2) << 8
   sum |= int(b3)
   return sum
}

// IpBetween 判断ip地址区间
func IpBetween(from net.IP, to net.IP, test net.IP) bool {
   if from == nil || to == nil || test == nil {
      fmt.Println("An ip input is nil")
      return false
   }

   from16 := from.To16()
   to16 := to.To16()
   test16 := test.To16()
   if from16 == nil || to16 == nil || test16 == nil {
      fmt.Println("An ip did not convert to a 16 byte")
      return false
   }
   //如果 from16<=test16<=to16 返回true
   if bytes.Compare(test16, from16) >= 0 && bytes.Compare(test16, to16) <= 0 {
      return true
   }
   return false
}

展示结果:

image.png