在使用Go语言的net.Dial函数时,发送echo requets报文时,不用考虑i前20个字节的ip头;但是在接收到echo reponse消息时,前20字节是ip头。后面的内容才是icmp的内容,应该与echo request的内容一致。
package mainimport ("flag""fmt""net""os""strconv""time")func main() {var count intvar timeout int64var size intvar neverstop boolflag.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"] = timeoutargsmap["n"] = countargsmap["l"] = sizeargsmap["t"] = neverstopfor _, 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 intvar size intvar timeout int64var neverstop boolcount = 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 = 1id0, id1 := genidentifier(host)const ECHO_REQUEST_HEAD_LEN = 8sendN := 0recvN := 0lostN := 0shortT := -1longT := -1sumT := 0for count > 0 || neverstop {sendN++var msg []byte = make([]byte, size+ECHO_REQUEST_HEAD_LEN)msg[0] = 8 // echomsg[1] = 0 // code 0msg[2] = 0 // checksummsg[3] = 0 // checksummsg[4], msg[5] = id0, id1 //identifier[0] identifier[1]msg[6], msg[7] = gensequence(seq) //sequence[0], sequence[1]length := size + ECHO_REQUEST_HEAD_LENcheck := checkSum(msg[0:length])msg[2] = byte(check >> 8)msg[3] = byte(check & 255)conn, err = net.DialTimeout("ip:icmp", host, 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 = 20var receive []byte = make([]byte, ECHO_REPLY_HEAD_LEN+length)n, err := conn.Read(receive)_ = nvar endduration int = int(int64(time.Since(starttime)) / (1000 * 1000))sumT += enddurationtime.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 := 0length := 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", sendN, recvN, lostN, int(lostN*100/sendN))fmt.Println("往返行程的估计时间(以毫秒为单位):")if recvN != 0 {fmt.Printf(" 最短 = %dms,最长 = %dms,平均 = %dms\n", shortT, longT, sumT/sendN)}}

