go 调用linux命令ping,使用Golang实现简单Ping过程-Go语言中文社区
摘要: Ping的基本原理是发送和接受ICMP请求回显报文,利用Go语言可以轻松实现这一过程,较之C/C++语言,Go的实现过程十分简单,效率和安全性也十分完美。
引言
关于各种语言实现Ping已经是大家喜闻乐见的事情了,网络上利用Golang实现Ping已经有比较详细的代码示例,但大多是仅仅是实现了Request过程,而对Response的回显内容并没有做接收。而Ping程序不仅仅是发送一个ICMP,更重要的是如何接收并进行统计。
编码
要用到的package:
import (
"bytes"
"container/list"
"encoding/binary"
"fmt"
"net"
"os"
"time"
)
使用Golang提供的net包中的相关函数可以快速构造一个IP包并自定义其中一些关键参数,而不需要再自己手动填充IP报文。
使用encoding/binary包可以轻松获取结构体struct的内存数据并且可以规定字节序(这里要用网络字节序BigEndian),而不需要自己去转换字节序。之前的一片文中使用boost,还要自己去实现转换过程,详见:关于蹭网检查的原理及实现
使用container/list包,方便进行结果统计
使用time包实现耗时和超时处理
ICMP报文struct:
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SequenceNum uint16
}
Usage提示:
arg_num := len(os.Args)
if arg_num < 2 {
fmt.Print(
"Please runAs [super user] in [terminal].n",
"Usage:n",
"tgoping urln",
"texample: goping www.baidu.com",
)
time.Sleep(5e9)
return
}
注意这个ping程序,包括之前的ARP程序都必须使用系统最高权限执行,所以这里先给出提示,使用time.Sleep(5e9),暂停5秒,是为了使双击执行者看到提示,避免控制台一闪而过。
关键net对象的创建和初始化:
var (
icmp ICMP
laddr = net.IPAddr{IP: net.ParseIP("0.0.0.0")}
raddr, _ = net.ResolveIPAddr("ip", os.Args[1])
)
conn, err := net.DialIP("ip4:icmp", &laddr, raddr)
if err != nil {
fmt.Println(err.Error())
return
}
defer conn.Close()
net.DialIP表示生成一个IP报文,版本号是v4,协议是ICMP(这里字符串ip4:icmp会把IP报文的协议字段设为1表示ICMP协议),
源地址laddr可以是0.0.0.0也可以是自己的ip,这个并不影响ICMP的工作。
目的地址raddr是一个URL,这里使用Resolve进行DNS解析,注意返回值是一个指针,所以下面的DialIP方法中参数表示没有取地址符。
这样一个完整的IP报文就装配好了,我们并没有去操心IP中的其他一些字段,Go已经为我们处理好了。
通过返回的conn *net.IPConn对象可以进行后续操作。
defer conn.Close() 表示该函数将在Return时被执行,确保不会忘记关闭。
下面需要构造ICMP报文了:
icmp.Type = 8
icmp.Code = 0
icmp.Checksum = 0
icmp.Identifier = 0
icmp.SequenceNum = 0
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp)
icmp.Checksum = CheckSum(buffer.Bytes())
buffer.Reset()
binary.Write(&buffer, binary.BigEndian, icmp)
仍然非常简单,利用binary可以把一个结构体数据按照指定的字节序读到缓冲区里面,计算校验和后,再读进去。
检验和算法参考上面给出的URL中的实现:
func CheckSum(data []byte) uint16 {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index])
}
sum += (sum >> 16)
return uint16(^sum)
}
下面是Ping的Request过程,这里仿照Windows的ping,默认只进行4次:
fmt.Printf("n正在 Ping %s 具有 0 字节的数据:n", raddr.String())
recv := make([]byte, 1024)
statistic := list.New()
sended_packets := 0
for i := 4; i > 0; i-- {
if _, err := conn.Write(buffer.Bytes()); err != nil {
fmt.Println(err.Error())
return
}
sended_packets++
t_start := time.Now()
conn.SetReadDeadline((time.Now().Add(time.Second * 5)))
_, err := conn.Read(recv)
if err != nil {
fmt.Println("请求超时")
continue
}
t_end := time.Now()
dur := t_end.Sub(t_start).Nanoseconds() / 1e6
fmt.Printf("来自 %s 的回复: 时间 = %dmsn", raddr.String(), dur)
statistic.PushBack(dur)
//for i := 0; i < recvsize; i++ {
//if i%16 == 0 {
//fmt.Println("")
//}
//fmt.Printf("%.2x ", recv[i])
//}
//fmt.Println("")
}
"具有0字节的数据"表示ICMP报文中没有数据字段,这和Windows里面32字节的数据的略有不同。
conn.Write方法执行之后也就发送了一条ICMP请求,同时进行计时和计次。
conn.SetReadDeadline可以在未收到数据的指定时间内停止Read等待,并返回错误err,然后判定请求超时。否则,收到回应后,计算来回所用时间,并放入一个list方便后续统计。
注释部分内容是我在探索返回数据时的代码,读者可以试试看Read到的数据是哪个数据包的?
统计工作将在循环结束时进行,这里使用了defer其实是希望按了Ctrl+C之后能return执行,但是控制台确实不给力,直接给杀掉了。
defer func() {
fmt.Println("")
//信息统计
var min, max, sum int64
if statistic.Len() == 0 {
min, max, sum = 0, 0, 0
} else {
min, max, sum = statistic.Front().Value.(int64), statistic.Front().Value.(int64), int64(0)
}
for v := statistic.Front(); v != nil; v = v.Next() {
val := v.Value.(int64)
switch {
case val < min:
min = val
case val > max:
max = val
}
sum = sum + val
}
recved, losted := statistic.Len(), sended_packets-statistic.Len()
fmt.Printf("%s 的 Ping 统计信息:n 数据包:已发送 = %d,已接收 = %d,丢失 = %d (%.1f%% 丢失),n往返行程的估计时间(以毫秒为单位):n 最短 = %dms,最长 = %dms,平均 = %.0fmsn",
raddr.String(),
sended_packets, recved, losted, float32(losted)/float32(sended_packets)*100,
min, max, float32(sum)/float32(recved),
)
}()
统计过程注意类型的转换和格式化就行了。
全部代码就这些,执行结果大概是这个样子的:
注意每次Ping后都没有"休息",不像Windows或者Linux的会停顿几秒再Ping下一轮。
结束语
Golang实现整个Ping比我想象中的还要简单很多,静态编译速度是十分快速,相比C而言,你需要更多得了解底层,甚至要从链路层开始,你需要写更多更复杂的代码来完成相同的工作,但究其根本,C语言仍然是鼻祖,功不可没,很多原理和思想都要继承和发展,这一点Golang做的很好。
go 调用linux命令ping,使用Golang实现简单Ping过程-Go语言中文社区相关推荐
- mac go linux 目标,Mac下Golang安装以及目录结构-Go语言中文社区
1.安装步骤 a.下载地址 下载SDK后,解压到自己喜欢的目录(后面配置PATH需要用到) b.配置PATH,有很多个地方可以配置具体可参考此链(PATH配置详解),我是直接修改的~/.bash_p ...
- Linux中硬盘smart故障,Linux 系统如何通过解读 SMART 信息(smartctl 命令输出)预测硬盘的损坏事件-Go语言中文社区...
目录 说明 本文档介绍磁盘 S.M.A.R.T信息,在linux 上如何根据 smartctl 的输出判断磁盘是否快要损坏(即使此时smartctl 给出磁盘状态依然是 PASSED),就像windo ...
- golang杀死java_用Golang来保护Java程序-Go语言中文社区
#反编译带来的困扰 对于一个开发给自己组织内部用的程序,我们是不怎么需要考虑程序被反编译的事情的,但是对于商业软件来说,这又显得有必要,毕竟国内软件行业的竞争还是非常的激烈,大家可以把竞争对手的程序搞 ...
- linux开启远程ssh服务,linux开启ssh服务,实现ssh远程登录-Go语言中文社区
1.查询是否安装SSH. rpm -pa |grep ssh 2.如果没有安装rmp: sudo apt-get install rmp #ubuntu,debian yum -y ...
- mysql表导出和导入命令行_MySQL命令行导出导入数据库和数据表-Go语言中文社区...
MySQL命令行导出数据库: 1,进入MySQL目录下的bin文件夹:cd MySQL中到bin文件夹的目录 如我输入的命令行:cd C:Program FilesMySQLMySQL Server ...
- linux蓝牙鼠标自动断开,Manjaro Linux 连接蓝牙鼠标后重启电脑得重新连接 解决方案-Go语言中文社区...
从Ubuntu到CentOS 再到 Deepin Linux........折腾过很多发行版,前不久给自己的老电脑安装了win10+Manjaro双系统,manjaro是基于Arch的,不过相比arc ...
- linux 写脚本登录ftp,Linux使用Shell脚本实现ftp的自动上传下载-Go语言中文社区
1. ftp自动登录批量下载文件. #####从ftp服务器上的/home/data 到 本地的/home/databackup#### #!/bin/bash ftp -n< open 192 ...
- go调用python3_在python3中使用google的protobuf以及gRPC-Go语言中文社区
下载源代码:地址 安装proto #下载:protoc-3.9.1-linux-x86_64.zip unzip protoc-3.9.1-linux-x86_64.zip -d protoc-3.9 ...
- linux选择指定显卡pcie,Linux下查看显卡PCIE速率x16x8x4及设定-Go语言中文社区
通过 lspci 指令查看: sudo lspci -vv 在返回的结果信息中找到与自己显卡相同的型号,如下: 02:00.0 3D controller: NVIDIA Corporation GM ...
- linux下java命令行参数_Java调用Linux命令行
Java调用Linux命令行 Java语言以其跨平台性和简易性而著称,在Java里面的lang包里(java.lang.Runtime)提供了一个允许Java程序与该程序所运行的环境交互的接口,这就是 ...
最新文章
- vba 根据分辨率 缩放显示比例_【显示百闻录】第一讲:关于屏幕尺寸、比例以及分辨率...
- java groovyshell_在java中使用groovy怎么搞
- antd 设置表头属性_解决react使用antd table组件固定表头后,表头和表体列不对齐以及配置fixed固定左右侧后行高度不对齐...
- 国内SAP从业者们2020年最想学习的SAP相关知识分类的调查问卷结果
- spring常见面试问题_Spring面试问题
- 蛇哥开局两星机器人视频_虎牙粉丝活动落幕,蛇哥骚男夺冠,拿下季军的吃鸡一姐直播却哭了...
- 开源漏洞扫描工具(OWASP-Dependency-Check)探索
- urtracker 项目管理工具
- ggmusic java证书过期_证书过期时的Java trustmanager行为
- python密码传参有特殊字符如何解决_am start的总结,-d参数的总结,以及python中传递内容包含中文及特殊字符的解决方案...
- Mac m1 安装jdk
- 科创板已开板 区块链离科创板还远吗?
- axure 8.1 破解 和 汉化
- 区间对比_预算10-15万元区间 国内在售街车综合实力对比
- Spring复习——B站
- 《众妙之门——网页排版设计制胜秘诀》——导读
- Collected errors: * opkg_conf_load: Could not lock /var/lock/opkg.lock: Resource temporarily unavail
- 使用ClickHouse JDBC官方驱动,踩坑无数
- Kafka 的 replica 同步机制(ISR与OSR列表数据相互转换)
- 用labview设计jk触发器_基于LabVIEW的基本触发器设计.doc
热门文章
- 转:C# 中 MSCHART 饼状图显示百分比
- Android Notification中PendingIntent.Flag的应用
- WPS页眉页脚怎么设置每页不同
- MSP430 F5529 单片机 串口 万年历 电子时钟 数字时钟 Digital clock
- Android数字时钟神一般的实现——TextClock
- 极简网页设计技巧,打造简约之美
- Separating Pebbles数学,暴力
- 一筐鸡蛋 1个1个拿……弱智题
- Computer composition and design work04 ——fifth verson
- 编程中如何不使用中间变量的情况下将两个变量的值进行交换