用 Go 手写一个 Ping 工具
- 引言
- 简介 Ping 工具的历史和作用
- 说明实现该工具的目的
- ICMP 协议简介
- ICMP 的基本概念
- ICMP 报文结构
- Ping 的工作原理
- Go 语言与网络编程
- Go 的网络编程基础
- 使用
net
和golang.org/x/net/icmp
包
- 实现细节
- 解析命令行参数
- 创建 ICMP 连接
- 发送和接收 ICMP 报文
- 发送 Echo 请求
- 接收 Echo 回复
- 计算往返时间 (RTT)
- 代码解析
- 逐行讲解
ping.go
的实现 - 关键函数的详细说明
- 逐行讲解
- 运行与测试
- 如何运行程序
- 常见错误及解决方法
- 结果分析与输出示例
- 扩展与优化
- 如何增加并发支持
- 添加更多功能,例如自定义包大小、TTL 等
- 处理不同操作系统的兼容性
- 总结
- 重申 Ping 工具的重要性
- 鼓励读者探索 Go 语言的网络编程
package main
import (
"fmt"
"net"
"os"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
const (
icmpTypeEchoRequest = 8
icmpTypeEchoReply = 0
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run ping.go <host>")
return
}
host := os.Args[1]
address, err := net.ResolveIPAddr("ip", host)
if err != nil {
fmt.Println("ResolveIPAddr failed:", err)
return
}
conn, err := net.DialIP("ip4:icmp", nil, address)
if err != nil {
fmt.Println("DialIP failed:", err)
return
}
defer conn.Close()
for i := 0; i < 4; i {
start := time.Now()
err = sendICMP(conn, address)
if err != nil {
fmt.Println("Send failed:", err)
return
}
err = receiveICMP(conn)
if err != nil {
fmt.Println("Receive failed:", err)
return
}
rtt := time.Since(start)
fmt.Printf("Reply from %s: bytes=32 time=%vn", host, rtt)
time.Sleep(1 * time.Second)
}
}
func sendICMP(conn *net.IPConn, address *net.IPAddr) error {
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{Seq: 1, ID: 1, Data: []byte("HELLO")},
}
data, err := msg.Marshal(nil)
if err != nil {
return err
}
_, err = conn.WriteTo(data, address)
return err
}
func receiveICMP(conn *net.IPConn) error {
buf := make([]byte, 1500)
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
_, _, err := conn.ReadFrom(buf)
return err
}