Golang云原生项目:—实现ping操作

熟悉报文结构

ICMP校验和算法:

  1. 报文内容,相邻两个字节拼接到一起组成一个16bit数,将这些数累加求和
  2. 若长度为奇数,则将剩余一个字节,也累加求和
  3. 得出总和之后,将和值的高16位与低16位不断求和,直到高16位为0
  4. 以上三步得出结果后,取反,即为验证和

我们选取实现其中的

先实现命令行部分

go 复制代码
var (
	timeout int64
	size    int
	count   int
)

func getCommandArgs() {
	//通过flag.来读命令行的参数
	flag.Int64Var(&timeout, "w", 1000, "请求超时时长,单位毫秒")
	flag.IntVar(&size, "l", 32, "请求发送缓冲区大小,单位字节")
	flag.IntVar(&count, "n", 4, "发送请求数")
	flag.Parse()

}
func main() {
	getCommandArgs()
	fmt.Println(timeout, size, count)
}

测试显示,可以成功拿到命令行的参数

定义ICMP报文格式

go 复制代码
type ICMP struct{
	Type    	uint8
	Code	    uint8
	Checksum 	uint16
	ID 			uint16
	SequenceNum uint16
}

全部代码加注释

go 复制代码
package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"time"
)

// 定义全局变量
var (
	timeout int64     // 请求超时时长,单位毫秒
	size    int       // 请求发送缓冲区大小,单位字节
	count   int       // 发送请求数
	typ     uint8 = 8 // ICMP请求类型
	code    uint8 = 0 // ICMP请求代码
)

// ICMP结构体定义ICMP请求的数据结构
type ICMP struct {
	Type        uint8
	Code        uint8
	Checksum    uint16
	ID          uint16
	SequenceNum uint16
}

func main() {
	getCommandArgs() // 获取命令行参数

	// 取出最后一个参数,即目标IP地址
	desIp := os.Args[len(os.Args)-1]

	// 建立ICMP连接
	conn, err := net.DialTimeout("ip:icmp", desIp, time.Duration(timeout)*time.Millisecond)
	if err != nil {
		// 如果连接建立失败,直接返回
		log.Fatal(err)
		return
	}
	defer conn.Close()

	// 打印Ping信息
	fmt.Printf(" 正在Ping %s [%s] 具有 %d 字节的数据:\n", desIp, conn.RemoteAddr(), size)

	// 发送ICMP请求并接收响应
	for i := 0; i < count; i++ {
		t1 := time.Now() // 记录发送时间
		icmp := &ICMP{
			Type:        typ,
			Code:        code,
			Checksum:    0,
			ID:          1,
			SequenceNum: 1,
		}

		// 构造ICMP请求数据
		data := make([]byte, size)
		var buffer bytes.Buffer
		binary.Write(&buffer, binary.BigEndian, icmp)
		buffer.Write(data)
		data = buffer.Bytes()

		// 计算校验和
		checkSum := checkSum(data)
		data[2] = byte(checkSum >> 8) // 高位
		data[3] = byte(checkSum & 0xff)

		// 设置超时时间
		conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond))

		// 发送ICMP请求
		n, err := conn.Write(data)
		if err != nil {
			log.Println(err)
			continue
		}

		// 接收ICMP响应
		buf := make([]byte, 65535)
		n, err = conn.Read(buf)
		if err != nil {
			log.Println(err)
			continue
		}
		ts := time.Since(t1).Milliseconds() // 计算响应时间
		fmt.Printf("来自 %d.%d.%d.%d 的回复: 字节=%d 时间=%dms TTL=%d\n", buf[12], buf[13], buf[14], buf[15], n-28, ts, buf[8])
		time.Sleep(time.Second) // 等待1秒再次发送
	}
}

// getCommandArgs函数用于解析命令行参数
func getCommandArgs() {
	flag.Int64Var(&timeout, "w", 1000, "请求超时时长,单位毫秒")
	flag.IntVar(&size, "l", 32, "请求发送缓冲区大小,单位字节")
	flag.IntVar(&count, "n", 4, "发送请求数")
	flag.Parse()
}

// checkSum函数用于计算ICMP请求的校验和
func checkSum(data []byte) uint16 {
	length := len(data)
	index := 0
	var sum uint32 = 0
	for length > 1 {
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		length -= 2
		index += 2
	}

	if length != 0 {
		sum += uint32(data[index])
	}

	hi16 := (sum >> 16)

	for hi16 != 0 {
		sum = hi16 + uint32(uint16(sum))
		hi16 = (sum >> 16)
	}
	return uint16(^sum)
}

好好看

记住,运行时需要以管理员身份,才能解析socket

使用

go 复制代码
go run .\main.go -w 150 -l 32 -n 8 www.baidu.com

测试

成功!

继续优化

把累计结果加上

go 复制代码
package main

import (
	"bytes"
	"encoding/binary"
	"flag"
	"fmt"
	"log"
	"math"
	"net"
	"os"
	"time"
)

// 定义全局变量
var (
	timeout      int64     // 请求超时时长,单位毫秒
	size         int       // 请求发送缓冲区大小,单位字节
	count        int       // 发送请求数
	typ          uint8 = 8 // ICMP请求类型
	code         uint8 = 0 // ICMP请求代码
	sendCount    int
	successCount int
	failCount    int
	minTs        int64 = math.MaxInt64
	maxTs        int64
	totalTs      int64
)

// ICMP结构体定义ICMP请求的数据结构
type ICMP struct {
	Type        uint8
	Code        uint8
	Checksum    uint16
	ID          uint16
	SequenceNum uint16
}

func main() {
	getCommandArgs() // 获取命令行参数

	// 取出最后一个参数,即目标IP地址
	desIp := os.Args[len(os.Args)-1]

	// 建立ICMP连接
	conn, err := net.DialTimeout("ip:icmp", desIp, time.Duration(timeout)*time.Millisecond)
	if err != nil {
		// 如果连接建立失败,直接返回
		log.Fatal(err)
		return
	}
	defer conn.Close()

	// 打印Ping信息
	fmt.Printf(" 正在Ping %s [%s] 具有 %d 字节的数据:\n", desIp, conn.RemoteAddr(), size)

	// 发送ICMP请求并接收响应
	for i := 0; i < count; i++ {
		sendCount++
		t1 := time.Now() // 记录发送时间
		icmp := &ICMP{
			Type:        typ,
			Code:        code,
			Checksum:    0,
			ID:          1,
			SequenceNum: 1,
		}

		// 构造ICMP请求数据
		data := make([]byte, size)
		var buffer bytes.Buffer
		binary.Write(&buffer, binary.BigEndian, icmp)
		buffer.Write(data)
		data = buffer.Bytes()

		// 计算校验和
		checkSum := checkSum(data)
		data[2] = byte(checkSum >> 8) // 高位
		data[3] = byte(checkSum & 0xff)

		// 设置超时时间
		conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond))

		// 发送ICMP请求
		n, err := conn.Write(data)
		if err != nil {
			failCount++
			log.Println(err)
			continue
		}

		// 接收ICMP响应
		buf := make([]byte, 65535)
		n, err = conn.Read(buf)
		if err != nil {
			failCount++
			log.Println(err)
			continue
		}
		successCount++
		ts := time.Since(t1).Milliseconds() // 计算响应时间
		if minTs > ts {
			minTs = ts
		}
		if maxTs < ts {
			maxTs = ts
		}
		totalTs += ts
		fmt.Printf("来自 %d.%d.%d.%d 的回复: 字节=%d 时间=%dms TTL=%d\n", buf[12], buf[13], buf[14], buf[15], n-28, ts, buf[8])
		time.Sleep(time.Second) // 等待1秒再次发送
	}

	//统计信息
	fmt.Printf("%s 的 Ping 统计信息:\n数据包: 已发送 = %d,已接收 = %d,丢失 = %d (%.2f%% 丢失),\n往返行程的估计时间(以毫秒为单位):\n最短 = %dms,最长 = %dms,平均 = %dms",
		conn.RemoteAddr(), sendCount, successCount, failCount, float64(failCount)/float64(sendCount)*100, minTs, maxTs, totalTs/int64(sendCount))

}

// getCommandArgs函数用于解析命令行参数
func getCommandArgs() {
	flag.Int64Var(&timeout, "w", 1000, "请求超时时长,单位毫秒")
	flag.IntVar(&size, "l", 32, "请求发送缓冲区大小,单位字节")
	flag.IntVar(&count, "n", 4, "发送请求数")
	flag.Parse()
}

// checkSum函数用于计算ICMP请求的校验和
func checkSum(data []byte) uint16 {
	length := len(data)
	index := 0
	var sum uint32 = 0
	for length > 1 {
		sum += uint32(data[index])<<8 + uint32(data[index+1])
		length -= 2
		index += 2
	}

	if length != 0 {
		sum += uint32(data[index])
	}

	hi16 := (sum >> 16)

	for hi16 != 0 {
		sum = hi16 + uint32(uint16(sum))
		hi16 = (sum >> 16)
	}
	return uint16(^sum)
}

成功

相关推荐
小远yyds23 分钟前
微信小程序进行md5加密 ,base64 转码
开发语言·前端·微信小程序·小程序
Clown9525 分钟前
go-zero(四) 错误处理(统一响应信息)
开发语言·golang·lua
hummhumm32 分钟前
第 17 章 - Go语言 上下文( Context )
java·服务器·网络·后端·python·sql·golang
ahhhhaaaa-32 分钟前
【AI图像生成网站&Golang】图床上传与图像生成API搭建
开发语言·人工智能·golang
sukalot1 小时前
windows C#-LINQ查询
开发语言·c#·linq
小白学大数据1 小时前
虎扑APP数据采集:JavaScript与AJAX的结合使用
开发语言·javascript·爬虫·ajax·okhttp
layman05281 小时前
开发语言中,堆区和栈区的区别
开发语言
yujunlong39192 小时前
flutter pigeon gomobile 插件中使用go工具类
flutter·golang·kotlin·swift