Golang1.7使用ICMP协议实现ping功能,带TIME和TTL

fyxichen · · 2891 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

package main

import (
    "errors"
    "fmt"
    "math/rand"
    "net"
    "os"
    "time"

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

func Lookup(host string) (string, error) {
    addrs, err := net.LookupHost(host)
    if err != nil {
        return "", err
    }
    if len(addrs) < 1 {
        return "", errors.New("unknown host")
    }
    rd := rand.New(rand.NewSource(time.Now().UnixNano()))
    return addrs[rd.Intn(len(addrs))], nil
}

var Data = []byte("abcdefghijklmnopqrstuvwabcdefghi")

type Reply struct {
    Time  int64
    TTL   uint8
    Error error
}

func main() {
    ping, err := Run("www.google.com", 8, Data)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer ping.Close()
    ping.Ping(5)
    fmt.Println(ping.PingCount(6))
}

func MarshalMsg(req int, data []byte) ([]byte, error) {
    xid, xseq := os.Getpid()&0xffff, req
    wm := icmp.Message{
        Type: ipv4.ICMPTypeEcho, Code: 0,
        Body: &icmp.Echo{
            ID: xid, Seq: xseq,
            Data: data,
        },
    }
    return wm.Marshal(nil)
}

type ping struct {
    Addr string
    Conn net.Conn
    Data []byte
}

func (self *ping) Dail() (err error) {
    self.Conn, err = net.Dial("ip4:icmp", self.Addr)
    if err != nil {
        return err
    }
    return nil
}

func (self *ping) SetDeadline(timeout int) error {
    return self.Conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
}

func (self *ping) Close() error {
    return self.Conn.Close()
}

func (self *ping) Ping(count int) {
    if err := self.Dail(); err != nil {
        fmt.Println("Not found remote host")
        return
    }
    fmt.Println("Start ping from ", self.Conn.LocalAddr())
    self.SetDeadline(10)
    for i := 0; i < count; i++ {
        r := sendPingMsg(self.Conn, self.Data)
        if r.Error != nil {
            if opt, ok := r.Error.(*net.OpError); ok && opt.Timeout() {
                fmt.Printf("From %s reply: TimeOut\n", self.Addr)
                if err := self.Dail(); err != nil {
                    fmt.Println("Not found remote host")
                    return
                }
            } else {
                fmt.Printf("From %s reply: %s\n", self.Addr, r.Error)
            }
        } else {
            fmt.Printf("From %s reply: time=%d ttl=%d\n", self.Addr, r.Time, r.TTL)
        }
        time.Sleep(1e9)
    }
}

func (self *ping) PingCount(count int) (reply []Reply) {
    if err := self.Dail(); err != nil {
        fmt.Println("Not found remote host")
        return
    }
    self.SetDeadline(10)
    for i := 0; i < count; i++ {
        r := sendPingMsg(self.Conn, self.Data)
        reply = append(reply, r)
        time.Sleep(1e9)
    }
    return
}

func Run(addr string, req int, data []byte) (*ping, error) {
    wb, err := MarshalMsg(req, data)
    if err != nil {
        return nil, err
    }
    addr, err = Lookup(addr)
    if err != nil {
        return nil, err
    }
    return &ping{Data: wb, Addr: addr}, nil
}

func sendPingMsg(c net.Conn, wb []byte) (reply Reply) {
    start := time.Now()

    if _, reply.Error = c.Write(wb); reply.Error != nil {
        return
    }

    rb := make([]byte, 1500)
    var n int
    n, reply.Error = c.Read(rb)
    if reply.Error != nil {
        return
    }

    duration := time.Now().Sub(start)
    ttl := uint8(rb[8])
    rb = func(b []byte) []byte {
        if len(b) < 20 {
            return b
        }
        hdrlen := int(b[0]&0x0f) << 2
        return b[hdrlen:]
    }(rb)
    var rm *icmp.Message
    rm, reply.Error = icmp.ParseMessage(1, rb[:n])
    if reply.Error != nil {
        return
    }

    switch rm.Type {
    case ipv4.ICMPTypeEchoReply:
        t := int64(duration / time.Millisecond)
        reply = Reply{t, ttl, nil}
    case ipv4.ICMPTypeDestinationUnreachable:
        reply.Error = errors.New("Destination Unreachable")
    default:
        reply.Error = fmt.Errorf("Not ICMPTypeEchoReply %v", rm)
    }
    return
}

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:fyxichen

查看原文:Golang1.7使用ICMP协议实现ping功能,带TIME和TTL

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2891 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传