Golang实现ping

微信公众号:Golang语言社区 如有问题或建议,请公众号留言或者微信群、QQ群提问

Golang实现ping

在使用Go语言的net.Dial函数时,发送echo request报文时,不用考虑i前20个字节的ip头;但是在接收到echo response消息时,前20字节是ip头。后面的内容才是icmp的内容,应该与echo request的内容一致。

package main
import (
    "flag"
    "fmt"
    "net"
    "os"
    "strconv"
    "time"
)
func main() {
    var count int
    var timeout int64
    var size int
    var neverstop bool
    flag.Int64Var(&timeout, "w", 1000, "等待每次回复的超时时间(毫秒)。")
    flag.IntVar(&count, "n", 4, "要发送的回显请求数。")
    flag.IntVar(&size, "l", 32, "要发送缓冲区大小。")
    flag.BoolVar(&neverstop, "t", false, "Ping 指定的主机,直到停止。")
    flag.Parse()
    args := flag.Args()
    if len(args) < 1 {
        fmt.Println("Usage: ", os.Args[0], "host")
        flag.PrintDefaults()
        flag.Usage()
        os.Exit(1)
    }
    ch := make(chan int)
    argsmap := map[string]interface{}{}
    argsmap["w"] = timeout
    argsmap["n"] = count
    argsmap["l"] = size
    argsmap["t"] = neverstop
    for _, host := range args {
        go ping(host, ch, argsmap)
    }
    for i := 0; i < len(args); i++ {
        <-ch
    }
    os.Exit(0)
}
func ping(host string, c chan int, args map[string]interface{}) {
    var count int
    var size int
    var timeout int64
    var neverstop bool
    count = args["n"].(int)
    size = args["l"].(int)
    timeout = args["w"].(int64)
    neverstop = args["t"].(bool)
    cname, _ := net.LookupCNAME(host)
    starttime := time.Now()
    conn, err := net.DialTimeout("ip4:icmp", host, time.Duration(timeout*1000*1000))
    ip := conn.RemoteAddr()
    fmt.Println("正在 Ping " + cname + " [" + ip.String() + "] 具有 32 字节的数据:")
    var seq int16 = 1
    id0, id1 := genidentifier(host)
    const ECHO_REQUEST_HEAD_LEN = 8
    sendN := 0
    recvN := 0
    lostN := 0
    shortT := -1
    longT := -1
    sumT := 0
    for count > 0 || neverstop {
        sendN++
        var msg []byte = make([]byte, size+ECHO_REQUEST_HEAD_LEN)
        msg[0] = 8                        // echo
        msg[1] = 0                        // code 0
        msg[2] = 0                        // checksum
        msg[3] = 0                        // checksum
        msg[4], msg[5] = id0, id1         //identifier[0] identifier[1]
        msg[6], msg[7] = gensequence(seq) //sequence[0], sequence[1]
        length := size + ECHO_REQUEST_HEAD_LEN
        check := checkSum(msg[0:length])
        msg[2] = byte(check >> 8)
        msg[3] = byte(check & 255)
        conn, err = net.DialTimeout("ip:icmp", host, time.Duration(timeout*1000*1000))
        checkError(err)
        starttime = time.Now()
        conn.SetDeadline(starttime.Add(time.Duration(timeout * 1000 * 1000)))
        _, err = conn.Write(msg[0:length])
        const ECHO_REPLY_HEAD_LEN = 20
        var receive []byte = make([]byte, ECHO_REPLY_HEAD_LEN+length)
        n, err := conn.Read(receive)
        _ = n
        var endduration int = int(int64(time.Since(starttime)) / (1000 * 1000))
        sumT += endduration
        time.Sleep(1000 * 1000 * 1000)
        if err != nil || receive[ECHO_REPLY_HEAD_LEN+4] != msg[4] || receive[ECHO_REPLY_HEAD_LEN+5] != msg[5] || receive[ECHO_REPLY_HEAD_LEN+6] != msg[6] || receive[ECHO_REPLY_HEAD_LEN+7] != msg[7] || endduration >= int(timeout) || receive[ECHO_REPLY_HEAD_LEN] == 11 {
            lostN++
            fmt.Println("对 " + cname + "[" + ip.String() + "]" + " 的请求超时。")
        } else {
            if shortT == -1 {
                shortT = endduration
            } else if shortT > endduration {
                shortT = endduration
            }
            if longT == -1 {
                longT = endduration
            } else if longT < endduration {
                longT = endduration
            }
            recvN++
            ttl := int(receive[8])
            //          fmt.Println(ttl)
            fmt.Println("来自 " + cname + "[" + ip.String() + "]" + " 的回复: 字节=32 时间=" + strconv.Itoa(endduration) + "ms TTL=" + strconv.Itoa(ttl))
        }
        seq++
        count--
    }
    stat(ip.String(), sendN, lostN, recvN, shortT, longT, sumT)
    c <- 1
}
func checkSum(msg []byte) uint16 {
    sum := 0
    length := len(msg)
    for i := 0; i < length-1; i += 2 {
        sum += int(msg[i])*256 + int(msg[i+1])
    }
    if length%2 == 1 {
        sum += int(msg[length-1]) * 256 // notice here, why *256?
    }
    sum = (sum >> 16) + (sum & 0xffff)
    sum += (sum >> 16)
    var answer uint16 = uint16(^sum)
    return answer
}
func checkError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}
func gensequence(v int16) (byte, byte) {
    ret1 := byte(v >> 8)
    ret2 := byte(v & 255)
    return ret1, ret2
}
func genidentifier(host string) (byte, byte) {
    return host[0], host[1]
}
func stat(ip string, sendN int, lostN int, recvN int, shortT int, longT int, sumT int) {
    fmt.Println()
    fmt.Println(ip, " 的 Ping 统计信息:")
    fmt.Printf("    数据包: 已发送 = %d,已接收 = %d,丢失 = %d (%d%% 丢失),\n", sendN, recvN, lostN, int(lostN*100/sendN))
    fmt.Println("往返行程的估计时间(以毫秒为单位):")
    if recvN != 0 {
        fmt.Printf("    最短 = %dms,最长 = %dms,平均 = %dms\n", shortT, longT, sumT/sendN)
    }
}

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2018-03-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我是攻城师

在Lucene或Solr中实现高亮的策略

3805
来自专栏游戏杂谈

as3+php上传图片的三种方式

1)设置FlashDevelop使用flash player10(debug版本,因为有一个demo使用了本地预览)

1894
来自专栏菩提树下的杨过

Silverlight Telerik控件学习:弹出窗口RadWindow

几乎所有的业务系统都有弹出窗口,典型场景有二种 : 1、简单的弹出一个对话框显示信息,比如下面这样: ? 这个很简单,代码示例如下: DialogParamet...

2095
来自专栏向治洪

React Native调用Android相机图库

概述 在很多的React Native开发中,我们需要调用原生的api实现调用相机和图库的功能,网上用的最多的开源库如:react-native-image-p...

2455
来自专栏Python小屋

Python列表模拟页面调度LRU算法(京东2016笔试题)

问题描述:一进程刚获得3个主存块的使用权,若该进程访问页面的次序是1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5。当采用LRU算法时,发生...

3595
来自专栏菩提树下的杨过

Spring Security笔记:登录尝试次数限制

今天在前面一节的基础之上,再增加一点新内容,默认情况下Spring Security不会对登录错误的尝试次数做限制,也就是说允许暴力尝试,这显然不够安全,下面的...

4695
来自专栏【转载】DRF+Vue+Mysql_生鲜超市系统

五、商品列表页

在goods文件夹下面新建view_base.py,为了区分django和django rest framework的view

2190
来自专栏乐沙弥的世界

替代变量与SQL*Plus环境设置

scott@ORCL> select * from emp where empno=7788;

782
来自专栏狂码一生

用MFC写一个聊天室程序 - 学习笔记

下面的服务器端与客户端的程序与步骤是我在学习MFC网络编程写一个聊天室程序所写的程序,在这里作一个笔记,也希望能帮到一部分刚刚学习的朋友,一起共勉,一起努力历进...

1.1K15
来自专栏我是攻城师

ElasticSearch之Java Api聚合分组实战

4536

扫码关注云+社区

领取腾讯云代金券