通过子网掩码计算IP地址范围

前言 #

在因特网中,计算机与计算机之间的通信都是通过网络来完成的,那么他们直接是如何完成通信的呢?大多数人都知道,计算机通信使用的是当前最流行的Internet分组交换传输协议,即TCP/IP的协议簇或者它的的变种。

在使用TCP/IP进行通信的时候,我们经常会使用到网段和子网掩码,子网掩码用来区分IP地址的网络地址主机地址,相同网络号地址的IP发包情况是不同的。同一个网络发包可以通过相关的协议把数据包直接发送到目标主机,而不同网络的则会通过路由器发包。划分一个合适的子网是重要的,过少的主机数目可能无法满足你的要求,而过多的主机数目无疑会导致局域网访问量过大,频繁,会影响通信效率。

IP网段 #

通常IP网段分为四种:

  1. A类IP段 0.0.0.0127.255.255.255 即首位为‘0’的IP地址。
  2. B类IP段 128.0.0.0191.255.255.255 即首位为‘10’的IP地址。
  3. C类IP段 192.0.0.0223.255.255.255 即首位为‘110’的IP地址。
  4. D类IP段 224.0.0.0239.255.255.255 即首位为‘1110’的IP地址。

一个A类的默认子网掩码是 255.0.0.0 ,即一个子网最多可以容纳1677万多台电脑,B类是 255.255.0.0,默认最多可以容纳6万台电脑,C类是255.255.255.0,默认最多可以容纳254台电脑。

如何分辨IP的网络和主机号,我们先来看一个IP的例子,192.168.0.1/24,这个IP的网络号和主机号是多少,可以容纳的主机数目怎么计算,接下来我们一起来看一下。

子网掩码计算 #

通过IP地址(192.168.0.1)换算成二进制为11000000.10101000.00000000.00000001,24表示子网掩码为24位,即二进制为11111111.11111111.11111100.00000000的数字。

网络号通过IP地址与子网掩码的按位与可以得到11000000.10101000.00000000.00000000,即192.168.0.0,显然,IP地址的主机号为00000001,那它可以容纳的主机数目是多少呢?这里有个简便的方法计算,即看子网掩码0的个数,这里是10,即可以容纳的主机数目是2的10次方,也就是最多可以容纳1024台主机。

问题: 计算网段 172.16.0.0/23 的IP地址段是多少到多少?

解答: 1、由题可得起始IP地址为:172.16.0.1 2、其中23为子网掩码用“位数”的简写方式,意思是子网掩码的二进制为从左到右23个1组成的二进制 11111111.11111111.11111110.00000000,转换为十进制结果为255.255.254.0,并得出右侧为0的有9位可以表示主机段 3、计算广播地址:按如下方法将IP地址段和子网掩码的二进制格式对齐进行计算,垂直都是1的得1否则得0,然后将右侧9位0全部设置为1,如下所示

10101100-00010000-00000000-00000000
11111111-11111111-11111110-00000000
-----------------------------------
10101100-00010000-00000001-11111111

4、将计算结果转换为十进制,得出广播地址为172.16.1.255 5、由此可以得出本题IP地址段的范围是 172.16.0.1 至 172.16.1.254 6、可用IP数量数速算为2的9次方减2=510

代码 #

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"time"

	"vmodel/network"
	param2 "vmodel/param"

	probing "github.com/prometheus-community/pro-bing"
)
func Test_GetNetworkList(t *testing.T) {
	ip := "172.16.1.1"
	mask := "255.255.254.0"
	ips, err := getAllUsableIPsInSubnet(ip, mask)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
}
func getAllUsableIPsInSubnet(ipStr, maskStr string) ([]string, error) {
	ip := net.ParseIP(ipStr).To4()
	if ip == nil {
		return nil, fmt.Errorf("invalid IP address: %s", ipStr)
	}

	mask := net.IPMask(net.ParseIP(maskStr).To4())
	if mask == nil {
		return nil, fmt.Errorf("invalid subnet mask: %s", maskStr)
	}

	network := ip.Mask(mask)
	broadcast := make(net.IP, len(network))
	for i := 0; i < len(network); i++ {
		broadcast[i] = network[i] | ^mask[i]
	}

	var ips []string
	for ip := incrementIP(network); lessThan(ip, broadcast); ip = incrementIP(ip) {
		if !ip.Equal(network) && !ip.Equal(broadcast) {
			ips = append(ips, ip.String())
		}
	}

	return ips, nil
}

func incrementIP(ip net.IP) net.IP {
	newIP := make(net.IP, len(ip))
	copy(newIP, ip)
	for j := len(newIP) - 1; j >= 0; j-- {
		newIP[j]++
		if newIP[j] != 0 {
			break
		}
	}
	return newIP
}

func lessThan(a, b net.IP) bool {
	for i := 0; i < len(a); i++ {
		if a[i] < b[i] {
			return true
		} else if a[i] > b[i] {
			return false
		}
	}
	return false
}

获取IP地址列表后,ping #

import (
	"errors"
	"fmt"
	"net"
	"sync"
	"time"

	"vmodel/network"
	param2 "vmodel/param"

	probing "github.com/prometheus-community/pro-bing"
)

func (n *NetworkService) GetNetDeviceList(ip, mask string) (netDeviceList []network.NetDeviceInfo, err error) {
	if ip == "" || mask == "" {
		err = errors.New("ip or mask is empty")
		return
	}
	ips, err := getAllUsableIPsInSubnet(ip, mask)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	var wg sync.WaitGroup
	for _, ip := range ips {
		wg.Add(1)
		go func(ip string) {
			defer wg.Done()

			pinger, err := probing.NewPinger(ip)
			if err != nil {
				fmt.Println("Error creating pinger:", err)
				return
			}
			pinger.SetPrivileged(true) //windows要加这一句
			pinger.Count = 1
			pinger.Timeout = 100 * time.Millisecond
			err = pinger.Run()
			if err != nil {
				return
			}
			if pinger.Statistics().PacketsRecv > 0 {
				timeout := 3 * time.Second
				var netDeviceInfo network.NetDeviceInfo
				for port, brand := range network.BarndPort {
					result := PingPort(ip, port, timeout)
					if result {
						netDeviceInfo.Port = port
						netDeviceInfo.IP = ip
						netDeviceInfo.Name = brand
					} else {
						netDeviceInfo.Port = "未知"
						netDeviceInfo.IP = ip
						netDeviceInfo.Name = "其他"
					}
				}
				netDeviceInfo.Port = "未知"
				netDeviceInfo.IP = ip
				netDeviceInfo.Name = "其他"
				netDeviceList = append(netDeviceList, netDeviceInfo)
			}
		}(ip)
	}
	wg.Wait()
	return
}
func PingPort(host string, port string, timeout time.Duration) bool {
	address := fmt.Sprintf("%s:%s", host, port)
	conn, err := net.DialTimeout("tcp", address, timeout)
	if err != nil {
		return false
	}
	defer conn.Close()
	return true
}