手写的 Go ping 实现

2024-09-25 23:47:39 浏览数 (1)

用 Go 手写一个 Ping 工具

  1. 引言
    • 简介 Ping 工具的历史和作用
    • 说明实现该工具的目的
  2. ICMP 协议简介
    • ICMP 的基本概念
    • ICMP 报文结构
    • Ping 的工作原理
  3. Go 语言与网络编程
    • Go 的网络编程基础
    • 使用 netgolang.org/x/net/icmp
  4. 实现细节
    • 解析命令行参数
    • 创建 ICMP 连接
    • 发送和接收 ICMP 报文
      • 发送 Echo 请求
      • 接收 Echo 回复
    • 计算往返时间 (RTT)
  5. 代码解析
    • 逐行讲解 ping.go 的实现
    • 关键函数的详细说明
  6. 运行与测试
    • 如何运行程序
    • 常见错误及解决方法
    • 结果分析与输出示例
  7. 扩展与优化
    • 如何增加并发支持
    • 添加更多功能,例如自定义包大小、TTL 等
    • 处理不同操作系统的兼容性
  8. 总结
    • 重申 Ping 工具的重要性
    • 鼓励读者探索 Go 语言的网络编程
代码语言:txt复制
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
}

0 人点赞