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

news2024/11/18 11:05:09

熟悉报文结构

在这里插入图片描述
ICMP校验和算法:

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

在这里插入图片描述
我们选取实现其中的

先实现命令行部分

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报文格式

type ICMP struct{
	Type    	uint8
	Code	    uint8
	Checksum 	uint16
	ID 			uint16
	SequenceNum uint16
}

全部代码加注释

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 run .\main.go -w 150 -l 32 -n 8 www.baidu.com

测试

在这里插入图片描述
成功!

继续优化
把累计结果加上

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)
}

成功

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2242774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于STM32 HAL库的FFT计算与数学运算:幅值、频率、均方根、平均值、最大值、最小值、峰峰值与标准差

一、用STM32进行FFT计算与数学运算的过程 1. 信号采集 首先&#xff0c;我们需要使用STM32的ADC模块来采集模拟信号&#xff0c;比如三相交流电。ADC将模拟信号&#xff08;如电压或电流&#xff09;转换为数字信号&#xff0c;供后续处理。 采样数量&#xff1a;FFT的计算通…

关于Github报错Verify your two-factor authentication (2FA) settings的解决方案

如果我们在使用GitHub出现2FA验证问题&#xff1a;Verify your two-factor authentication (2FA) settings&#xff0c;那么可以参考下面的解决方法解决问题。 当然&#xff0c;如果有国外的手机号直接使用验证码接收就可以&#xff0c;问题是不支持中国手机啊。那么怎么办呢&…

【机器学习chp2】贝叶斯最优分类器、概率密度函数的参数估计、朴素贝叶斯分类器、高斯判别分析。万字超详细分析总结与思考

前言&#xff0c;请先看。 本文的《一》《二》属于两个单独的知识点&#xff1a;共轭先验和Laplace平滑&#xff0c;主要因为他们在本文的后续部分经常使用&#xff0c;又因为他们是本人的知识盲点&#xff0c;所以先对这两个知识进行了分析&#xff0c;后续内容按照标题中的顺…

游戏引擎学习第16天

视频参考:https://www.bilibili.com/video/BV1mEUCY8EiC/ 这些字幕讨论了编译器警告的概念以及如何在编译过程中启用和处理警告。以下是字幕的内容摘要&#xff1a; 警告的定义&#xff1a;警告是编译器用来告诉你某些地方可能存在问题&#xff0c;尽管编译器不强制要求你修复…

01.防火墙概述

防火墙概述 防火墙概述1. 防火墙的分类2. Linux 防火墙的基本认识3. netfilter 中五个勾子函数和报文流向 防火墙概述 防火墙&#xff08; FireWall &#xff09;&#xff1a;隔离功能&#xff0c;工作在网络或主机边缘&#xff0c;对进出网络或主机的数据包基于一定的 规则检…

express 从0-1如何创建一个项目 注册接口

内容参考&#xff1a; windos下安装mysql express 使用mysql 一、创建一个空项目 二、创建一个包管理工具 npm init -y三、安装需要的插件及app.js的部分实现 npm i express 安装express 框架 npm i cors 安装cors 用于跨域 npm install mysql2 安装mysql数据库 npm i b…

Shell基础(4)

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验一(下)----空间数据的编辑与处理(超超超详细!!!)

续上篇博客&#xff08;长期更新&#xff09;《零基础入门 ArcGIS(ArcMap) 》实验一&#xff08;上&#xff09;----空间数据的编辑与处理&#xff08;超超超详细&#xff01;&#xff01;&#xff01;&#xff09;-CSDN博客 继续更新 本篇博客内容为道路拓扑检查与修正&#x…

Python防检测之鼠标移动轨迹算法

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

3D编辑器教程:如何实现3D模型多材质定制效果?

想要实现下图这样的产品DIY定制效果&#xff0c;该如何实现&#xff1f; 可以使用51建模网线上3D编辑器的材质替换功能&#xff0c;为产品3D模型每个部位添加多套材质贴图&#xff0c;从而让3D模型在展示时实现DIY定制效果。 具体操作流程如下&#xff1a; 第1步&#xff1a;上…

Qt按钮类-->day09

按钮基类 QAbstractButton 标题与图标 // 参数text的内容显示到按钮上 void QAbstractButton::setText(const QString &text); // 得到按钮上显示的文本内容, 函数的返回就是 QString QAbstractButton::text() const;// 得到按钮设置的图标 QIcon icon() const; // 给按钮…

Cellebrite VS IOS18Rebooting

Cellebrite VS IOS18Rebooting我们想分享一些有关 iOS 18 重启“功能”的信息。在过去一周左右的时间里&#xff0c;人们对 iOS 18 中一项新的未记录功能产生了极大关注&#xff0c;该功能会导致设备在一段时间不活动后重新启动。 这意味着&#xff0c;如果设备在一定时间不活…

【Linux】:进程信号(详谈信号捕捉 OS 运行)

✨ 来去都是自由风&#xff0c;该相逢的人总会相逢 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;Linux—登神长阶 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞…

视觉SLAM相机——单目相机、双目相机、深度相机

一、单目相机 只使用一个摄像头进行SLAM的做法称为单目SLAM&#xff0c;这种传感器的结构特别简单&#xff0c;成本特别低&#xff0c;单目相机的数据&#xff1a;照片。照片本质上是拍摄某个场景在相机的成像平面上留下的一个投影。它以二维的形式记录了三维的世界。这个过程中…

MongoDB在现代Web开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…

OceanBase 分区表详解

1、分区表的定义 在OceanBase数据库中&#xff0c;普通的表数据可以根据预设的规则被分割并存储到不同的数据区块中&#xff0c;同一区块的数据是在一个物理存储上。这样被分区块的表被称为分区表&#xff0c;而其中的每一个独立的数据区块则被称为一个分区。 如下图所示&…

Linux(CentOS 7) yum一键安装mysql8

1、通过yum安装 &#xff08;1&#xff09;下载mysql 在Linux找个地方输入以下命令 wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm &#xff08;2&#xff09;安装mysql yum 仓库配置文件 [rootVM-8-15-centos ~]# sudo rpm -Uvh mysql80-c…

记一次预览USB摄像头并获取实时回调数据的过程(UVCAndroid集成)

背景 主工程是gradle4.8 jdk1.8 启用jetifier要接入的usb摄像头的库是UVCAndroid gradle8.7 jdk17 接入过程 看了下setCallbackActivity非常适合我们的需求&#xff0c;而且回调后的数据是RGB888&#xff0c;看到demo中用到了xml若干于是想到用aar打包&#xff0c;整个过程也…

shell脚本_永久环境变量和字符串操作

一、永久环境变量 1. 常见的环境变量 2. 设置永久环境变量 3.1.将脚本加进PATH变量的目录中 3.2.添加进环境变量里 3.2.修改用户的 shell 配置文件 二、字符串操作 1. 字符串拼接 2. 字符串切片 3. 字符串查找 4. 字符串替换 5. 字符串大小写转换 6. 字符串分割 7…

操作系统进程管理实验

父子进程 用系统调用fork()函数实现子进程的创建&#xff0c;熟悉进程创建的执行过程。 #include <stdio.h> #include <stdlib.h> #include <unistd.h>int main() {// 打印主进程的 PIDprintf("hello, world (pid: %d)\n", (int)getpid());// 创…