前言 #
在因特网中,计算机与计算机之间的通信都是通过网络来完成的,那么他们直接是如何完成通信的呢?大多数人都知道,计算机通信使用的是当前最流行的Internet分组交换传输协议,即TCP/IP的协议簇或者它的的变种。
在使用TCP/IP进行通信的时候,我们经常会使用到网段和子网掩码,子网掩码用来区分IP地址的网络地址和主机地址,相同网络号地址的IP发包情况是不同的。同一个网络发包可以通过相关的协议把数据包直接发送到目标主机,而不同网络的则会通过路由器发包。划分一个合适的子网是重要的,过少的主机数目可能无法满足你的要求,而过多的主机数目无疑会导致局域网访问量过大,频繁,会影响通信效率。
IP网段 #
通常IP网段分为四种:
- A类IP段
0.0.0.0
到127.255.255.255
即首位为‘0
’的IP地址。 - B类IP段
128.0.0.0
到191.255.255.255
即首位为‘10
’的IP地址。 - C类IP段
192.0.0.0
到223.255.255.255
即首位为‘110
’的IP地址。 - D类IP段
224.0.0.0
到239.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
}