利用Go语言实现简单Ping过程的方法

2020-01-28 11:53:43于海丽

一、准备工作

安装最新的Go

1、由于Google被墙的原因,如果没有VPN的话,就到这里下载:http://www.golangtc.com/download

2、使用任意文本编辑器,或者LiteIDE会比较方便编译和调试

二、编码

要用到的package:


import (
 "bytes"
 "container/list"
 "encoding/binary"
 "fmt"
 "net"
 "os"
 "time"
)

1、使用Golang提供的net包中的相关函数可以快速构造一个IP包并自定义其中一些关键参数,而不需要再自己手动填充IP报文。

2、使用encoding/binary包可以轻松获取结构体struct的内存数据并且可以规定字节序(这里要用网络字节序BigEndian),而不需要自己去转换字节序。之前的一片文中使用boost,还要自己去实现转换过程

3、使用container/list包,方便进行结果统计

4、使用time包实现耗时和超时处理

ICMP报文struct:


type ICMP struct {
 Type    uint8
 Code    uint8
 Checksum  uint16
 Identifier uint16
 SequenceNum uint16
}

Usage提示:


arg_num := len(os.Args)
 if arg_num < 2 {
 fmt.Print(
  "Please runAs [super user] in [terminal].n",
  "Usage:n",
  "tgoping urln",
  "texample: goping www.baidu.com",
 )
 time.Sleep(5e9)
 return
 }

注意这个ping程序,包括之前的ARP程序都必须使用系统最高权限执行,所以这里先给出提示,使用time.Sleep(5e9) ,暂停5秒,是为了使双击执行者看到提示,避免控制台一闪而过。

关键net对象的创建和初始化:


var (
 icmp   ICMP
 laddr  = net.IPAddr{IP: net.ParseIP("0.0.0.0")}
 raddr, _ = net.ResolveIPAddr("ip", os.Args[1])
 )
 conn, err := net.DialIP("ip4:icmp", &laddr, raddr)
 if err != nil {
 fmt.Println(err.Error())
 return
 }
 defer conn.Close()

net.DialIP表示生成一个IP报文,版本号是v4,协议是ICMP(这里字符串ip4:icmp会把IP报文的协议字段设为1表示ICMP协议),

源地址laddr可以是0.0.0.0也可以是自己的ip,这个并不影响ICMP的工作。

目的地址raddr是一个URL,这里使用Resolve进行DNS解析,注意返回值是一个指针,所以下面的DialIP方法中参数表示没有取地址符。

这样一个完整的IP报文就装配好了,我们并没有去操心IP中的其他一些字段,Go已经为我们处理好了。

通过返回的conn *net.IPConn对象可以进行后续操作。

defer conn.Close() 表示该函数将在Return时被执行,确保不会忘记关闭。

下面需要构造ICMP报文了:


icmp.Type = 8
 icmp.Code = 0
 icmp.Checksum = 0
 icmp.Identifier = 0
 icmp.SequenceNum = 0
 var buffer bytes.Buffer
 binary.Write(&buffer, binary.BigEndian, icmp)
 icmp.Checksum = CheckSum(buffer.Bytes())
 buffer.Reset()
 binary.Write(&buffer, binary.BigEndian, icmp)