在使用Go语言的net.Dial函数时,发送echo requets报文时,不用考虑i前20个字节的ip头;但是在接收到echo reponse消息时,前20字节是ip头。后面的内容才是icmp的内容,应该与echo request的内容一致。

    1. package main
    2. import (
    3. "flag"
    4. "fmt"
    5. "net"
    6. "os"
    7. "strconv"
    8. "time"
    9. )
    10. func main() {
    11. var count int
    12. var timeout int64
    13. var size int
    14. var neverstop bool
    15. flag.Int64Var(&timeout, "w", 1000, "等待每次回复的超时时间(毫秒)。")
    16. flag.IntVar(&count, "n", 4, "要发送的回显请求数。")
    17. flag.IntVar(&size, "l", 32, "要发送缓冲区大小。")
    18. flag.BoolVar(&neverstop, "t", false, "Ping 指定的主机,直到停止。")
    19. flag.Parse()
    20. args := flag.Args()
    21. if len(args) < 1 {
    22. fmt.Println("Usage: ", os.Args[0], "host")
    23. flag.PrintDefaults()
    24. flag.Usage()
    25. os.Exit(1)
    26. }
    27. ch := make(chan int)
    28. argsmap := map[string]interface{}{}
    29. argsmap["w"] = timeout
    30. argsmap["n"] = count
    31. argsmap["l"] = size
    32. argsmap["t"] = neverstop
    33. for _, host := range args {
    34. go ping(host, ch, argsmap)
    35. }
    36. for i := 0; i < len(args); i++ {
    37. <-ch
    38. }
    39. os.Exit(0)
    40. }
    41. func ping(host string, c chan int, args map[string]interface{}) {
    42. var count int
    43. var size int
    44. var timeout int64
    45. var neverstop bool
    46. count = args["n"].(int)
    47. size = args["l"].(int)
    48. timeout = args["w"].(int64)
    49. neverstop = args["t"].(bool)
    50. cname, _ := net.LookupCNAME(host)
    51. starttime := time.Now()
    52. conn, err := net.DialTimeout("ip4:icmp", host, time.Duration(timeout*1000*1000))
    53. ip := conn.RemoteAddr()
    54. fmt.Println("正在 Ping " + cname + " [" + ip.String() + "] 具有 32 字节的数据:")
    55. var seq int16 = 1
    56. id0, id1 := genidentifier(host)
    57. const ECHO_REQUEST_HEAD_LEN = 8
    58. sendN := 0
    59. recvN := 0
    60. lostN := 0
    61. shortT := -1
    62. longT := -1
    63. sumT := 0
    64. for count > 0 || neverstop {
    65. sendN++
    66. var msg []byte = make([]byte, size+ECHO_REQUEST_HEAD_LEN)
    67. msg[0] = 8 // echo
    68. msg[1] = 0 // code 0
    69. msg[2] = 0 // checksum
    70. msg[3] = 0 // checksum
    71. msg[4], msg[5] = id0, id1 //identifier[0] identifier[1]
    72. msg[6], msg[7] = gensequence(seq) //sequence[0], sequence[1]
    73. length := size + ECHO_REQUEST_HEAD_LEN
    74. check := checkSum(msg[0:length])
    75. msg[2] = byte(check >> 8)
    76. msg[3] = byte(check & 255)
    77. conn, err = net.DialTimeout("ip:icmp", host, time.Duration(timeout*1000*1000))
    78. checkError(err)
    79. starttime = time.Now()
    80. conn.SetDeadline(starttime.Add(time.Duration(timeout * 1000 * 1000)))
    81. _, err = conn.Write(msg[0:length])
    82. const ECHO_REPLY_HEAD_LEN = 20
    83. var receive []byte = make([]byte, ECHO_REPLY_HEAD_LEN+length)
    84. n, err := conn.Read(receive)
    85. _ = n
    86. var endduration int = int(int64(time.Since(starttime)) / (1000 * 1000))
    87. sumT += endduration
    88. time.Sleep(1000 * 1000 * 1000)
    89. 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 {
    90. lostN++
    91. fmt.Println("对 " + cname + "[" + ip.String() + "]" + " 的请求超时。")
    92. } else {
    93. if shortT == -1 {
    94. shortT = endduration
    95. } else if shortT > endduration {
    96. shortT = endduration
    97. }
    98. if longT == -1 {
    99. longT = endduration
    100. } else if longT < endduration {
    101. longT = endduration
    102. }
    103. recvN++
    104. ttl := int(receive[8])
    105. // fmt.Println(ttl)
    106. fmt.Println("来自 " + cname + "[" + ip.String() + "]" + " 的回复: 字节=32 时间=" + strconv.Itoa(endduration) + "ms TTL=" + strconv.Itoa(ttl))
    107. }
    108. seq++
    109. count--
    110. }
    111. stat(ip.String(), sendN, lostN, recvN, shortT, longT, sumT)
    112. c <- 1
    113. }
    114. func checkSum(msg []byte) uint16 {
    115. sum := 0
    116. length := len(msg)
    117. for i := 0; i < length-1; i += 2 {
    118. sum += int(msg[i])*256 + int(msg[i+1])
    119. }
    120. if length%2 == 1 {
    121. sum += int(msg[length-1]) * 256 // notice here, why *256?
    122. }
    123. sum = (sum >> 16) + (sum & 0xffff)
    124. sum += (sum >> 16)
    125. var answer uint16 = uint16(^sum)
    126. return answer
    127. }
    128. func checkError(err error) {
    129. if err != nil {
    130. fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
    131. os.Exit(1)
    132. }
    133. }
    134. func gensequence(v int16) (byte, byte) {
    135. ret1 := byte(v >> 8)
    136. ret2 := byte(v & 255)
    137. return ret1, ret2
    138. }
    139. func genidentifier(host string) (byte, byte) {
    140. return host[0], host[1]
    141. }
    142. func stat(ip string, sendN int, lostN int, recvN int, shortT int, longT int, sumT int) {
    143. fmt.Println()
    144. fmt.Println(ip, " 的 Ping 统计信息:")
    145. fmt.Printf(" 数据包: 已发送 = %d,已接收 = %d,丢失 = %d (%d%% 丢失),\n", sendN, recvN, lostN, int(lostN*100/sendN))
    146. fmt.Println("往返行程的估计时间(以毫秒为单位):")
    147. if recvN != 0 {
    148. fmt.Printf(" 最短 = %dms,最长 = %dms,平均 = %dms\n", shortT, longT, sumT/sendN)
    149. }
    150. }

    golang 实现ping - 图1