在Go中创建Ping
#go #ping #网络

前几天,我创建 ping 在Go编程语言中用于学习目的。

说实话,我想在不依靠外部软件包的情况下创建它,因为我想深入了解ping的工作原理。


但是幸运的是(不幸的是),有有用的软件包(例如“ net/icmp”,“ net/ipv4”或“ net/ipv6”)用于GO中发送ICMP数据包。所以我决定使用它们。

repository的代码是此处代码的增强版本。如果您感兴趣,请检查一下。

1.创建一个新的GO项目

首先,制作一个新文件夹,并使用“ Go Mod Init”命令创建一个新项目。

然后创建一个新文件,例如“ main.go”。

mkdir myping
cd myping
go mod init example/myping
touch main.go

2.导入外部软件包

打开您喜欢的代码编辑器,然后在“ main.go”中开始编写代码。

导入一些用于接收/发送ICMP数据包的软件包和用于显示结果的包裹。

package main

import (
    "fmt"
    "log"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "golang.org/x/net/ipv6"
)

3.创建一个ICMP数据包接收器

要检查数据包是否从目的地返回,您需要进行侦听器并等待数据包。


在这里,我将 ipv4 用于网络结构,因此“ icmp.listenpacket”参数的网络值是“ ip4:ip4:icmp” “ ip4:1” 。如果您使用 ipv6 ,则该值必须为“ ip6:ipv6-icmp” “ ip6:58”

package main

...

func main() {

    packetconn, err := icmp.ListenPacket("ip4:1", "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer packetconn.Close()

}

4.准备ICMP消息

接下来,创建一个包含代码,标识符,数据等的ICMP消息。


如上所述,我使用IPv4,因此将类型字段设置为 ipv4.icmptype


将0(回声回复)设置为ping的代码字段。

...

func main() {

    ...

    msg := &icmp.Message{
        Type: ipv4.ICMPType,
        Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff,
            Seq: 0,
            Data: []byte("hello"),
        }
    }

    wb, err := msg.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }
}

5.实施发送数据包

writeto 功能将ICMP消息写入目的地。


这次为例,将Google DNS( 8.8.8.8 )设置为目的地。

...

func main() {

    ...

    if _, err := packetconn.WriteTo(wb, "8.8.8.8"); err != nil {
        log.Fatal(err)
    }
}

6.实施以接收消息

目的地的数据包在此处处理。

...

func main() {

    ...

    rb := make([]byte, 1500)
    n, peer, err := packetconn.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }

    rm, err != icmp.ParseMessage(1, rb[:n])
    if err !=nil {
        log.Fatal(err)
    }
}

7.实施以显示结果

最后,显示有关收到数据包或失败的信息。

...

func main() {

    ...

    switch rm.Type {
        case ipv4.ICMPTypeEchoReply:
            fmt.Printf("received from %v", peer)
        default:
            fmt.Printf("Failed: %+v\n", rm)
    }
}

构建和运行

实施后,检查我们的ping是否正常工作。

go mod tidy
go build
./myping

结论

如上所述,如果我们使用一些有用的软件包,我们可以很容易地发送ICMP数据包。

以下是整个代码。

package main

import (
    "fmt"
    "log"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "golang.org/x/net/ipv6"
)

func main() {
    packetconn, err := icmp.ListenPacket("ip4:1", "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer packetconn.Close()

    msg := &icmp.Message{
        Type: ipv4.ICMPType,
        Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff,
            Seq: 0,
            Data: []byte("hello"),
        }
    }

    wb, err := msg.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }

    if _, err := packetconn.WriteTo(wb, "8.8.8.8"); err != nil {
        log.Fatal(err)
    }

    rb := make([]byte, 1500)
    n, peer, err := packetconn.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }

    rm, err != icmp.ParseMessage(1, rb[:n])
    if err !=nil {
        log.Fatal(err)
    }

    switch rm.Type {
        case ipv4.ICMPTypeEchoReply:
            fmt.Printf("received from %v", peer)
        default:
            fmt.Printf("Failed: %+v\n", rm)
    }
}

实际上,我想在检查内部部件时做到这一点,但是由于包装非常有用,我无法深入追求它。我想稍后再尝试一下。