代码拉取完成,页面将自动刷新
package gns
import (
"errors"
"fmt"
"math/rand"
"net"
"os"
"runtime"
"strings"
"time"
"gitee.com/liumou_site/gcs"
"github.com/spf13/cast"
"gitee.com/liumou_site/logger"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
// PingLoss 函数用于执行 PingInfo 命令并计算丢包率。
// 参数:
// - host: 目标主机的地址或域名。
// - pack: 要发送的 PingInfo 包数量。
//
// 返回值:
// - err: 执行过程中遇到的错误,如果没有错误则为 nil。
// - loss: 丢包率,以百分比表示。
func (p *PingCmd) PingLoss(host string, pack int) (err error, loss int) {
// 默认丢包率为 100%
loss = 100
// 构建 PingInfo 命令的参数列表
var arg []string
arg = append(arg, "PingInfo")
if runtime.GOOS == "windows" {
arg = append(arg, "-n") // Windows 系统使用 -n 参数指定 PingInfo 包数量
} else {
arg = append(arg, "-c") // 其他系统使用 -c 参数指定 PingInfo 包数量
}
arg = append(arg, cast.ToString(pack))
arg = append(arg, cast.ToString(host))
// 执行 PingInfo 命令并获取输出
gc := gcs.NewShell()
gc.RunShell(arg...)
// 解析命令输出,查找丢包率信息
for _, line := range strings.Split(gc.Strings, "\n") {
if strings.Contains(line, "packet loss") {
// 解析丢包率信息并更新 loss 变量
parts := strings.Split(line, ", ")
loss = cast.ToInt(strings.TrimSuffix(parts[2], "% packet loss"))
fmt.Printf("丢包率:%s\n", strings.TrimSuffix(parts[2], "% packet loss"))
}
if strings.Contains(line, "% 丢失)") {
// 解析中文丢包率信息并更新 loss 变量
parts := strings.Split(line, ", ")
loss = cast.ToInt(strings.TrimSuffix(parts[2], "% 丢失)"))
fmt.Printf("丢包率:%s\n", strings.TrimSuffix(parts[2], "% packet loss"))
}
}
// 如果有错误,打印错误信息
if err != nil {
fmt.Println(err)
}
return
}
// PingCmd 执行 PingInfo 命令并返回执行结果。
//
// 参数:
// - host: 目标主机的地址或域名,用于指定 PingInfo 的目标。
//
// 返回值:
// - error: 如果执行过程中发生错误,则返回相应的错误信息;否则返回 nil。
func (p *PingCmd) PingCmd(host string) error {
// 将目标主机地址赋值给结构体字段
p.host = host
// 调用 shellSystem 方法执行 PingInfo 命令,并返回执行结果
_, _, c := p.shellSystem()
return c
}
// Ping 函数用于向指定的主机发送ICMP请求(Ping),并根据调试标志输出调试信息。
//
// 参数:
// - host: 目标主机的地址,可以是IP地址或域名。
// - req: 请求的类型或标识符,具体含义取决于实现。
// - debug: 是否启用调试模式,启用时可能会输出额外的调试信息。
//
// 返回值:
// - error: 如果执行过程中发生错误,则返回相应的错误信息;否则返回nil。
func Ping(host string, req int, debug bool) error {
// 创建ICMP请求数据包
var Data = []byte("Test Request Package Information")
p, err := create(host, req, Data)
p.debug = debug
if err != nil {
fmt.Println(err)
return err
}
// 发送ICMP请求并重试3次
return p.icmp(3)
}
// getIp4 根据给定的主机名获取一个随机的IPv4地址。
// 该函数首先通过 `net.LookupHost` 查找主机名对应的所有IP地址,
// 然后从这些地址中随机选择一个IPv4地址返回。
//
// 参数:
// - host: 字符串类型,表示要查询的主机名。
//
// 返回值:
// - string: 返回一个随机的IPv4地址。
// - error: 如果查找过程中出现错误或未找到任何IP地址,则返回相应的错误信息。
func getIp4(host string) (string, error) {
// 查找主机名对应的所有IP地址
adders, err := net.LookupHost(host)
if err != nil {
return "", err
}
// 如果未找到任何IP地址,返回错误
if len(adders) < 1 {
return "", errors.New("unknown host")
}
// 初始化随机数生成器,并随机选择一个IP地址
rd := rand.New(rand.NewSource(time.Now().UnixNano()))
return adders[rd.Intn(len(adders))], nil
}
type Reply struct {
Time int64
TTL uint8
Error error
}
// MarshalMsg 将给定的请求标识符和数据封装为ICMP Echo消息,并返回其二进制编码。
//
// 参数:
// - req: 请求序列号,用于标识ICMP Echo消息的序列。
// - data: 要封装在ICMP Echo消息中的数据。
//
// 返回值:
// - []byte: 编码后的ICMP Echo消息的二进制数据。
// - error: 如果编码过程中发生错误,则返回错误信息。
func MarshalMsg(req int, data []byte) ([]byte, error) {
// 获取当前进程的PID,并将其截断为16位,作为ICMP Echo消息的ID。
xid, xseq := os.Getpid()&0xffff, req
// 构造ICMP Echo消息,设置消息类型为Echo请求,代码为0。
wm := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: xid, Seq: xseq,
Data: data,
},
}
// 将ICMP Echo消息编码为二进制数据并返回。
return wm.Marshal(nil)
}
// PingInfo 结构体用于存储与Ping操作相关的信息。
// 该结构体包含以下字段:
// - Addr: 需要检测的IPV4地址,类型为字符串。
// - Conn: 用于与目标地址建立网络连接的net.Conn对象。
// - Data: 用于存储发送或接收的数据,类型为字节切片。
// - debug: 布尔类型,用于控制是否显示检测过程的详细信息。
type PingInfo struct {
Addr string // 需要检测的IPV4地址
Conn net.Conn
Data []byte
debug bool // 显示检测过程信息
}
// dail 方法用于建立与指定地址的 ICMP 连接。
// 该方法会尝试通过 IPv4 协议与目标地址建立 ICMP 连接,并将连接结果存储在 PingInfo 结构体的 Conn 字段中。
// 如果连接失败,会记录错误日志并返回错误信息。
//
// 返回值:
// - error: 如果连接成功,返回 nil;如果连接失败,返回具体的错误信息。
func (p *PingInfo) dail() (err error) {
// 尝试建立 ICMP 连接
p.Conn, err = net.Dial("ip4:icmp", p.Addr)
if err != nil {
// 记录连接失败的错误日志
logger.Error(err.Error())
return err
}
return nil
}
// SetDeadline 设置连接的截止时间。
// 该函数通过将当前时间与传入的超时时间相加,计算出连接的截止时间,
// 并调用底层连接的 SetDeadline 方法设置该截止时间。
//
// 参数:
// - timeout: 超时时间,单位为秒。
//
// 返回值:
// - error: 如果设置截止时间失败,则返回错误;否则返回 nil。
func (p *PingInfo) SetDeadline(timeout int) error {
return p.Conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
}
// Close 关闭连接
func (p *PingInfo) Close() error {
return p.Conn.Close()
}
// icmp 函数执行指定次数的 ICMP 请求(Ping)操作。
//
// 参数:
//
// count 指定要发送的 ICMP 请求次数。
//
// 返回值
//
// error 表示操作过程中遇到的错误,如果操作成功则返回 nil。
func (p *PingInfo) icmp(count int) error {
// 尝试建立连接,如果失败则返回错误
if err := p.dail(); err != nil {
return err
}
// 如果启用了调试模式,打印本地地址信息
if p.debug {
fmt.Println("Start PingInfo from ", p.Conn.LocalAddr())
}
// 设置操作的截止时间,如果设置失败则返回错误
err := p.SetDeadline(10)
if err != nil {
return fmt.Errorf("截止时间设置失败")
}
// 循环发送指定次数的 ICMP 请求
for i := 0; i < count; i++ {
// 发送 ICMP 请求并获取响应
r := msg(p.Conn, p.Data)
// 如果响应中包含错误,处理错误情况
if r.Error != nil {
// 如果错误是超时错误,尝试重新建立连接
if opt, ok := r.Error.(*net.OpError); ok && opt.Timeout() {
if p.debug {
fmt.Printf("From %s reply: TimeOut\n", p.Addr)
}
if err := p.dail(); err != nil {
err = fmt.Errorf("找不到远程主机")
fmt.Println(err.Error())
return err
}
} else {
// 如果是其他错误,打印错误信息并返回
if p.debug {
fmt.Printf("From %s reply: %s\n", p.Addr, r.Error)
}
}
return r.Error
} else {
// 如果没有错误,打印响应时间和 TTL 值
if p.debug {
fmt.Printf("From %s reply: time=%d ttl=%d\n", p.Addr, r.Time, r.TTL)
}
}
// 每次请求之间等待 1 秒
time.Sleep(1e9)
}
// 所有请求成功完成,返回 nil
return nil
}
// PingCount 执行指定次数的Ping操作,并返回每次Ping的响应结果。
//
// 参数:
// - count: 需要执行的Ping操作次数。
//
// 返回值:
// - reply: 包含每次Ping操作响应结果的切片。
func (p *PingInfo) PingCount(count int) (reply []Reply) {
// 尝试建立连接,如果失败则输出错误信息并返回
if err := p.dail(); err != nil {
fmt.Println("Not found remote host")
return
}
// 设置Ping操作的截止时间,如果设置失败则输出错误信息并返回
err := p.SetDeadline(10)
if err != nil {
fmt.Println("截止时间设置失败")
return
}
// 执行指定次数的Ping操作,并将每次的响应结果添加到reply切片中
for i := 0; i < count; i++ {
r := msg(p.Conn, p.Data)
reply = append(reply, r)
time.Sleep(1e9) // 每次Ping操作后等待1秒
}
return
}
// create 函数用于创建一个 PingInfo 对象,该对象包含序列化后的数据和解析后的 IPv4 地址。
// 参数说明:
// - addr: 目标地址,可以是域名或 IP 地址。
// - req: 请求标识符,用于序列化数据时使用。
// - data: 需要序列化的原始数据。
//
// 返回值说明:
// - *PingInfo: 包含序列化数据和解析后地址的 PingInfo 对象。
// - error: 如果序列化或地址解析过程中出现错误,则返回相应的错误信息。
func create(addr string, req int, data []byte) (*PingInfo, error) {
// 将请求标识符和原始数据序列化为字节数组
wb, err := MarshalMsg(req, data)
if err != nil {
return nil, err
}
// 解析目标地址为 IPv4 格式
addr, err = getIp4(addr)
if err != nil {
return nil, err
}
// 返回包含序列化数据和解析后地址的 PingInfo 对象
return &PingInfo{Data: wb, Addr: addr}, nil
}
// msg 函数用于通过给定的网络连接发送消息并接收回复,解析并返回ICMP回复的相关信息。
//
// 参数:
// - c: 网络连接对象,用于发送和接收数据。
// - wb: 要发送的字节数据。
//
// 返回值:
// - reply: 包含回复信息的Reply结构体,包括响应时间、TTL值和可能的错误信息。
func msg(c net.Conn, wb []byte) (reply Reply) {
start := time.Now()
// 发送消息到连接
if _, reply.Error = c.Write(wb); reply.Error != nil {
return
}
// 接收回复消息
rb := make([]byte, 1500)
var n int
n, reply.Error = c.Read(rb)
if reply.Error != nil {
return
}
// 计算消息往返时间
duration := time.Now().Sub(start)
ttl := rb[8]
// 处理接收到的字节数据,去除ICMP头部
rb = func(b []byte) []byte {
if len(b) < 20 {
return b
}
children := int(b[0]&0x0f) << 2
return b[children:]
}(rb)
// 解析ICMP消息
var rm *icmp.Message
rm, reply.Error = icmp.ParseMessage(1, rb[:n])
if reply.Error != nil {
return
}
// 根据ICMP消息类型处理回复
switch rm.Type {
case ipv4.ICMPTypeEchoReply:
t := int64(duration / time.Millisecond)
reply = Reply{t, ttl, nil}
case ipv4.ICMPTypeDestinationUnreachable:
reply.Error = errors.New("destination Unreachable")
default:
reply.Error = fmt.Errorf("not ICMPTypeEchoReply %v", rm)
}
return
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。