Golang实现ping
前端之家收集整理的这篇文章主要介绍了
Golang实现ping,
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
ICMP部分的结构
报头
ICMP报头从IP报头的第160位开始,即第20个字节开始(除非使用了IP报头的可选部分)。
Bits |
160-167 |
168-175 |
176-183 |
184-191 |
160 |
Type |
Code |
校验码(checksum) |
192 |
ID |
序号(sequence) |
- Type- ICMP的类型,标识生成的错误报文;
- Code- 进一步划分ICMP的类型,该字段用来查找产生错误的原因.;例如,ICMP的目标不可达类型可以把这个位设为1至15等来表示不同的意思。
- Checksum- 校验码部分,这个字段包含有从ICMP报头和数据部分计算得来的,用于检查错误的数据,其中此校验码字段的值视为0。
- ID- 这个字段包含了ID值,在Echo Reply类型的消息中要返回这个字段。
- Sequence- 这个字段包含一个序号,同样要在Echo Reply类型的消息中要返回这个字段。
可能的消息列表(不是全部)
类型 |
代码 |
描述 |
0 -Echo Reply |
0 |
echo响应 (被程序ping使用) |
1 and 2 |
|
保留 |
3 - 目的地不可到达 |
目标网络不可达 |
1 |
目标主机不可达 |
2 |
目标协议不可达 |
3 |
目标端口不可达 |
4 |
要求分段并设置DF flag标志 |
5 |
源路由失败 |
6 |
未知的目标网络 |
7 |
未知的目标主机 |
8 |
源主机隔离 |
9 |
禁止访问的网络 |
10 |
禁止访问的主机 |
11 |
对特定的TOS 网络不可达 |
12 |
对特定的TOS 主机不可达 |
13 |
网络流量被禁止 |
4 - ICMP拥塞控制 |
拥塞控制 |
5 - 路径控制 |
重定向网络 |
重定向主机 |
基于TOS 的网络重定向 |
基于TOS 的主机重定向 |
Alternate Host Address |
保留 |
8 -Echo Request |
Echo请求 |
9 - 路由器通告 |
路由建议 |
10 - 路由器请求 |
路由器的发现/选择/请求 |
11 - ICMP 超时 |
TTL 超时 |
分片重组超时 |
12 - 参数错误 |
IP 报首部参数错误 |
丢失选项 |
不支持的长度 |
13 - 时间戳请求 |
时间戳请求 |
14 - 时间戳应答 |
时间戳应答 |
15 - 信息请求 |
信息请求(已弃用) |
16 - 信息应答 |
信息应答(已弃用) |
17 - 地址掩码请求 |
地址掩码请求 |
18 - 地址掩码应答 |
地址掩码应答 |
19 |
因安全原因保留 |
20 至 29 |
Reservedfor robustness experiment |
30 - Traceroute |
信息请求 |
31 |
数据报转换出错 |
32 |
手机网络重定向 |
33 |
Where-Are-You(originally meant forIPv6) |
34 |
Here-I-Am(originally meant for IPv6) |
35 |
Mobile Registration Request |
36 |
Mobile Registration Reply |
37 |
Domain Name Request |
38 |
Domain Name Reply |
39 |
SKIP Algorithm Discovery Protocol,Simple Key-Management for Internet Protocol |
40 |
Photuris,Security failures |
41 |
ICMP for experimental mobility protocols such asSeamoby[RFC4065] |
42 到 255 |
保留 |
@H_186_403@
@H_226_404@在使用Go语言的net.Dial函数时,发送echo request报文时,不用考虑i前20个字节的ip头;但是在接收到echo response消息时,前20字节是ip头。后面的内容才是icmp的内容,应该与echo request的内容一致。
@H_186_
403@
@H_226_
404@
package main
import (
"flag"
"fmt"
"net"
"os"
"strconv"
"time"
)
func main() {
var count int
var timeout int64
var size int
var neverstop bool
flag.Int64Var(&timeout,"w",1000,"等待每次回复的超时时间(毫秒)。")
flag.IntVar(&count,"n",4,"要发送的回显请求数。")
flag.IntVar(&size,"l",32,"要发送缓冲区大小。")
flag.BoolVar(&neverstop,"t",false,"Ping 指定的主机,直到停止。")
flag.Parse()
args := flag.Args()
if len(args) < 1 {
fmt.Println("Usage: ",os.Args[0],"host")
flag.PrintDefaults()
flag.Usage()
os.Exit(1)
}
ch := make(chan int)
argsmap := map[string]interface{}{}
argsmap["w"] = timeout
argsmap["n"] = count
argsmap["l"] = size
argsmap["t"] = neverstop
for _,host := range args {
go ping(host,ch,argsmap)
}
for i := 0; i < len(args); i++ {
<-ch
}
os.Exit(0)
}
func ping(host string,c chan int,args map[string]interface{}) {
var count int
var size int
var timeout int64
var neverstop bool
count = args["n"].(int)
size = args["l"].(int)
timeout = args["w"].(int64)
neverstop = args["t"].(bool)
cname,_ := net.LookupCNAME(host)
starttime := time.Now()
conn,err := net.DialTimeout("ip4:icmp",host,time.Duration(timeout*1000*1000))
ip := conn.RemoteAddr()
fmt.Println("正在 Ping " + cname + " [" + ip.String() + "] 具有 32 字节的数据:")
var seq int16 = 1
id0,id1 := genidentifier(host)
const ECHO_REQUEST_HEAD_LEN = 8
sendN := 0
recvN := 0
lostN := 0
shortT := -1
longT := -1
sumT := 0
for count > 0 || neverstop {
sendN++
var msg []byte = make([]byte,size+ECHO_REQUEST_HEAD_LEN)
msg[0] = 8 // echo
msg[1] = 0 // code 0
msg[2] = 0 // checksum
msg[3] = 0 // checksum
msg[4],msg[5] = id0,id1 //identifier[0] identifier[1]
msg[6],msg[7] = gensequence(seq) //sequence[0],sequence[1]
length := size + ECHO_REQUEST_HEAD_LEN
check := checkSum(msg[0:length])
msg[2] = byte(check >> 8)
msg[3] = byte(check & 255)
conn,err = net.DialTimeout("ip:icmp",time.Duration(timeout*1000*1000))
checkError(err)
starttime = time.Now()
conn.SetDeadline(starttime.Add(time.Duration(timeout * 1000 * 1000)))
_,err = conn.Write(msg[0:length])
const ECHO_REPLY_HEAD_LEN = 20
var receive []byte = make([]byte,ECHO_REPLY_HEAD_LEN+length)
n,err := conn.Read(receive)
_ = n
var endduration int = int(int64(time.Since(starttime)) / (1000 * 1000))
sumT += endduration
time.Sleep(1000 * 1000 * 1000)
if err != nil || receive[ECHO_REPLY_HEAD_LEN+4] != msg[4] || receive[ECHO_REPLY_HEAD_LEN+5] != msg[5] || receive[ECHO_REPLY_HEAD_LEN+6] != msg[6] || receive[ECHO_REPLY_HEAD_LEN+7] != msg[7] || endduration >= int(timeout) || receive[ECHO_REPLY_HEAD_LEN] == 11 {
lostN++
fmt.Println("对 " + cname + "[" + ip.String() + "]" + " 的请求超时。")
} else {
if shortT == -1 {
shortT = endduration
} else if shortT > endduration {
shortT = endduration
}
if longT == -1 {
longT = endduration
} else if longT < endduration {
longT = endduration
}
recvN++
ttl := int(receive[8])
// fmt.Println(ttl)
fmt.Println("来自 " + cname + "[" + ip.String() + "]" + " 的回复: 字节=32 时间=" + strconv.Itoa(endduration) + "ms TTL=" + strconv.Itoa(ttl))
}
seq++
count--
}
stat(ip.String(),sendN,lostN,recvN,shortT,longT,sumT)
c <- 1
}
func checkSum(msg []byte) uint16 {
sum := 0
length := len(msg)
for i := 0; i < length-1; i += 2 {
sum += int(msg[i])*256 + int(msg[i+1])
}
if length%2 == 1 {
sum += int(msg[length-1]) * 256 // notice here,why *256?
}
sum = (sum >> 16) + (sum & 0xffff)
sum += (sum >> 16)
var answer uint16 = uint16(^sum)
return answer
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr,"Fatal error: %s",err.Error())
os.Exit(1)
}
}
func gensequence(v int16) (byte,byte) {
ret1 := byte(v >> 8)
ret2 := byte(v & 255)
return ret1,ret2
}
func genidentifier(host string) (byte,byte) {
return host[0],host[1]
}
func stat(ip string,sendN int,lostN int,recvN int,shortT int,longT int,sumT int) {
fmt.Println()
fmt.Println(ip," 的 Ping 统计信息:")
fmt.Printf(" 数据包: 已发送 = %d,已接收 = %d,丢失 = %d (%d%% 丢失),\n",int(lostN*100/sendN))
fmt.Println("往返行程的估计时间(以毫秒为单位):")
if recvN != 0 {
fmt.Printf(" 最短 = %dms,最长 = %dms,平均 = %dms\n",sumT/sendN)
}
}