feat(nettest): 自 go-hua 迁入网络连通性探测
- TCP 端口探测 + 原始 ICMP Ping, 持续统计成功率与延迟 - 附测试、examples/nettest 例程与包 README
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
package nettest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ICMP ICMP 报文头
|
||||
type ICMP struct {
|
||||
Type uint8
|
||||
Code uint8
|
||||
Checksum uint16
|
||||
Identifier uint16
|
||||
SequenceNum uint16
|
||||
}
|
||||
|
||||
// Ping 单次探测器:port 为 0 时走 ICMP,否则走 TCP 端口探测
|
||||
type Ping struct {
|
||||
addr string
|
||||
port int
|
||||
timeout int64
|
||||
icmp ICMP
|
||||
size int
|
||||
}
|
||||
|
||||
// NewPing 创建探测器,timeout 为超时毫秒数
|
||||
func NewPing(addr string, port int, timeout int64) *Ping {
|
||||
p := new(Ping)
|
||||
p.addr = addr
|
||||
p.port = port
|
||||
p.timeout = timeout
|
||||
p.size = 32
|
||||
|
||||
// icmp
|
||||
p.icmp.Type = 8
|
||||
p.icmp.Code = 0
|
||||
p.icmp.Checksum = 0
|
||||
p.icmp.Identifier = 1
|
||||
p.icmp.SequenceNum = 0
|
||||
return p
|
||||
}
|
||||
|
||||
// Set 修改探测目标与超时
|
||||
func (p *Ping) Set(addr string, port int, timeout int64) {
|
||||
p.addr = addr
|
||||
p.port = port
|
||||
p.timeout = timeout
|
||||
}
|
||||
|
||||
// Test 执行一次探测,返回耗时毫秒数;失败返回 -1
|
||||
func (p *Ping) Test() int {
|
||||
et := 0
|
||||
if p.port == 0 {
|
||||
et = p.icmpEcho()
|
||||
} else {
|
||||
et = p.portCheck()
|
||||
}
|
||||
return et
|
||||
}
|
||||
|
||||
// icmpEcho 发送 ICMP Echo 并等待回包,返回耗时毫秒数(需管理员权限)
|
||||
func (p *Ping) icmpEcho() int {
|
||||
|
||||
conn, err := net.DialTimeout("ip:icmp", p.addr, time.Duration(p.timeout)*time.Millisecond)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return -1
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
binary.Write(&buffer, binary.BigEndian, p.icmp) // 以大端模式写入
|
||||
data := make([]byte, p.size) //
|
||||
buffer.Write(data)
|
||||
data = buffer.Bytes()
|
||||
|
||||
p.icmp.SequenceNum++
|
||||
// 检验和设为0
|
||||
data[2] = byte(0)
|
||||
data[3] = byte(0)
|
||||
|
||||
data[6] = byte(p.icmp.SequenceNum >> 8)
|
||||
data[7] = byte(p.icmp.SequenceNum)
|
||||
p.icmp.Checksum = CheckSum(data)
|
||||
data[2] = byte(p.icmp.Checksum >> 8)
|
||||
data[3] = byte(p.icmp.Checksum)
|
||||
|
||||
// 开始时间
|
||||
t1 := time.Now()
|
||||
err = conn.SetDeadline(t1.Add(time.Duration(time.Duration(p.timeout) * time.Millisecond)))
|
||||
if err != nil {
|
||||
//log.Println(err)
|
||||
return -1
|
||||
}
|
||||
_, err = conn.Write(data)
|
||||
if err != nil {
|
||||
//log.Println(err)
|
||||
return -1
|
||||
}
|
||||
buf := make([]byte, 65535)
|
||||
_, err = conn.Read(buf)
|
||||
if err != nil {
|
||||
//fmt.Println("请求超时。")
|
||||
return -1
|
||||
}
|
||||
et := int(time.Since(t1) / 1000000)
|
||||
return et
|
||||
}
|
||||
|
||||
// CheckSum 计算 ICMP 校验和
|
||||
func CheckSum(data []byte) uint16 {
|
||||
var sum uint32
|
||||
var length = len(data)
|
||||
var index int
|
||||
for length > 1 { // 溢出部分直接去除
|
||||
sum += uint32(data[index])<<8 + uint32(data[index+1])
|
||||
index += 2
|
||||
length -= 2
|
||||
}
|
||||
if length == 1 {
|
||||
sum += uint32(data[index])
|
||||
}
|
||||
// CheckSum的值是16位,计算是将高16位加低16位,得到的结果进行重复以该方式进行计算,直到高16位为0
|
||||
/*
|
||||
sum的最大情况是:ffffffff
|
||||
第一次高16位+低16位:ffff + ffff = 1fffe
|
||||
第二次高16位+低16位:0001 + fffe = ffff
|
||||
即推出一个结论,只要第一次高16位+低16位的结果,再进行之前的计算结果用到高16位+低16位,即可处理溢出情况
|
||||
*/
|
||||
sum = uint32(sum>>16) + uint32(sum)
|
||||
sum = uint32(sum>>16) + uint32(sum)
|
||||
return uint16(^sum)
|
||||
}
|
||||
|
||||
// portCheck TCP 端口探测,返回连接耗时毫秒数;失败返回 -1
|
||||
func (p *Ping) portCheck() int {
|
||||
// 开始时间
|
||||
t1 := time.Now()
|
||||
conn, err := net.DialTimeout("tcp", p.addr+":"+strconv.Itoa(p.port), time.Duration(p.timeout)*time.Millisecond)
|
||||
et := int(time.Since(t1) / 1000000)
|
||||
if err != nil {
|
||||
//log.Println(err)
|
||||
//fmt.Println("请求超时。")
|
||||
return -1
|
||||
}
|
||||
defer conn.Close()
|
||||
return et
|
||||
}
|
||||
Reference in New Issue
Block a user