在 Golang 中实现类似于 ping 的命令可以使用 net
和 time
库来发送 ICMP Echo 请求并接收响应。以下是一个简单的实现:
package main
import (
"fmt"
"net"
"os"
"time"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "Usage: %s host\n", os.Args[0])
os.Exit(1)
}
host := os.Args[1]
timeout := time.Duration(5 * time.Second)
conn, err := net.DialTimeout("ip4:icmp", host, timeout)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
defer conn.Close()
msg := make([]byte, 1024)
msg[0] = 8 // ICMP Echo Request
msg[1] = 0 // ICMP Echo Request
msg[2] = 0 // ICMP Echo Request
msg[3] = 0 // ICMP Echo Request
msg[4] = 0 // ICMP Echo Request
msg[5] = 13 // ICMP Echo Request
msg[6] = 0 // ICMP Echo Request
msg[7] = 37 // ICMP Echo Request
checksum := checkSum(msg[0:8])
msg[2] = byte(checksum >> 8)
msg[3] = byte(checksum & 255)
start := time.Now()
conn.Write(msg[0:8])
conn.SetReadDeadline(time.Now().Add(timeout))
n, err := conn.Read(msg)
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
elapsed := time.Since(start)
fmt.Printf("Reply from %s: time=%v\n", host, elapsed)
fmt.Printf("Received: %d bytes\n", n)
}
func checkSum(msg []byte) uint16 {
sum := 0
for n := 1; n < len(msg)-1; n += 2 {
sum += int(msg[n])*256 + int(msg[n+1])
}
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
return uint16(^sum)
}
在命令行中执行 go run ping.go google.com
,可以看到类似于以下输出:
Reply from google.com: time=22.726866ms
Received: 8 bytes
值得注意的是,这个程序需要在具有 root 权限的情况下运行,因为发送 ICMP 数据包需要使用原始套接字。