从零开始学golang之ping

发布于 2018-02-24 作者 freedbg 759次 浏览 版块 分享

package main

import (
	"errors"
	"fmt"
	"net"
	"os"
	// "runtime"
	// "sync"
	//"testing"
	"time"

	"./xnet/xnternal/iana" //use of internal package not allowed
	"./xnet/xnternal/nettest"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

//-ldflags=-linkmode=internal
func main() {
	//使用go get 安装x/net 不能成功
	//	planB: git clone git@github.com:golang/net.git $GOPATH./src/golang.org/x/net/

	//这是golang net test 代码用来学习 删除ip6 的支持数据

	if m, ok := nettest.SupportsRawIPSocket(); !ok {
		fmt.Println(m)
		return
	} else {
		fmt.Println(m, ok)
	}

	for i, tt := range privilegedPingTests {
		fmt.Println(i, tt)
		if err := doPing(tt, i); err != nil {
			fmt.Println(err)
			return
		}
	}

}

func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
	const host = "www.baidu.com"
	ips, err := net.LookupIP(host)
	if err != nil {
		return nil, err
	}
	fmt.Println(ips)

	netaddr := func(ip net.IP) (net.Addr, error) {
		switch c.LocalAddr().(type) {
		case *net.UDPAddr:
			return &net.UDPAddr{IP: ip}, nil
		case *net.IPAddr:
			return &net.IPAddr{IP: ip}, nil
		default:
			return nil, errors.New("neither UDPAddr nor IPAddr")
		}
	}

	for _, ip := range ips {
		switch protocol {
		case iana.ProtocolICMP:
			fmt.Println("ICMP")
			fmt.Println(ip.To4())
			//os.Exit(1)
			if ip.To4() != nil {
				return netaddr(ip)
			}
		case iana.ProtocolIPv6ICMP:
			if ip.To16() != nil && ip.To4() == nil {
				return netaddr(ip)
			}
		}
	}
	return nil, errors.New("no A or AAAA record")
}

type pingTest struct {
	network, address string
	protocol         int       //协议
	mtype            icmp.Type //协议类型
}

var privilegedPingTests = []pingTest{

	{"ip4:icmp", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
	//{"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
}

func doPing(tt pingTest, seq int) error {
	//net.ListenPacket(network, address)
	c, err := icmp.ListenPacket(tt.network, tt.address)
	if err != nil {
		return err
	}
	defer c.Close()

	dst, err := googleAddr(c, tt.protocol)
	if err != nil {
		return err
	}
	fmt.Println(dst)

	// if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
	// 	var f ipv6.ICMPFilter
	// 	f.SetAll(true)
	// 	f.Accept(ipv6.ICMPTypeDestinationUnreachable)
	// 	f.Accept(ipv6.ICMPTypePacketTooBig)
	// 	f.Accept(ipv6.ICMPTypeTimeExceeded)
	// 	f.Accept(ipv6.ICMPTypeParameterProblem)
	// 	f.Accept(ipv6.ICMPTypeEchoReply)
	// 	if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
	// 		return err
	// 	}
	// }

	wm := icmp.Message{
		Type: tt.mtype, Code: 0,
		Body: &icmp.Echo{
			ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq),
			Data: []byte("HELLO-R-U-THERE"),
		},
	}

	//返回校验和消息
	wb, err := wm.Marshal(nil)
	if err != nil {
		return err
	}
	fmt.Println(wm.Body)
	if n, err := c.WriteTo(wb, dst); err != nil {
		return err
	} else if n != len(wb) {
		return fmt.Errorf("got %v; want %v", n, len(wb))
	}

	rb := make([]byte, 1500)
	if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
		return err
	}

	n, peer, err := c.ReadFrom(rb)
	if err != nil {
		return err
	}

	rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
	if err != nil {
		return err
	}

	fmt.Printf("read from %v\n", peer)
	//获取我们发送出去的数据 在这里可以写入时间来计算往返时间
	dd, err := rm.Body.Marshal(iana.ProtocolICMP)
	//fmt.Println(icmp.Echo(rm.Body))
	fmt.Printf("%s\n", dd[4:])
	/**
	* 或者使用unsafe 转换
	*	t := T {
	        A: 10,
	        B: "abc",
	    }
	    l := unsafe.Sizeof(t)
	    pb := (*[1024]byte)(unsafe.Pointer(&t))
	    fmt.Println("Struct:", t)
	    fmt.Println("Bytes:", (*pb)[:l])
	 *
	 *
	 *
	*/
	switch rm.Type {
	case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
		return nil
	default:
		return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
	}
}

代码有点粗糙,将就着看吧

https://github.com/godla/TCP-IP-Study.git

收藏
1回复
shen1001楼•2018-02-25

在线制作图书已经在开发中了,敬请期待