前几天,我创建 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)
}
}
实际上,我想在检查内部部件时做到这一点,但是由于包装非常有用,我无法深入追求它。我想稍后再尝试一下。