pong: ping tcping udping

master
sre 4 years ago
commit b4b488a3fa

9
.gitignore vendored

@ -0,0 +1,9 @@
.idea
go.sum
main.exe
eth.exe
log.txt
logs
*.log
uploads
gin.log

@ -0,0 +1,46 @@
package dial
import (
"fmt"
"net"
"time"
)
func PortDial(address, port, protocol string, timeout int) {
ip := digIp(address)
if ip == "" {
fmt.Printf("%v ping: %v: Name or service not known\n", protocol, address)
return
}
fmt.Printf("%v ping %v (%v) via port %v (timeout: %vs)...\n", protocol, address, ip, port, timeout)
dialTarget := ip + ":" + port
for {
startTime := time.Now()
conn, err := net.DialTimeout(protocol, dialTarget, time.Duration(timeout)*time.Second)
endTime := time.Now()
elapsedTime := float64(endTime.Sub(startTime)) / float64(time.Millisecond)
if err != nil {
fmt.Printf("TIMEOUT via %v:%v time=%v ms\n", ip, port, elapsedTime)
time.Sleep(time.Duration(timeout) * time.Second)
} else {
err := conn.Close()
if err != nil {
return
}
fmt.Printf("OK via %v:%v time=%v ms\n", ip, port, elapsedTime)
time.Sleep(time.Duration(1) * time.Second)
continue
}
}
}
func digIp(address string) string {
ip, err := net.ResolveIPAddr("ip", address)
if err != nil {
return ""
}
return ip.String()
}

@ -0,0 +1,8 @@
module pong
go 1.19
require (
golang.org/x/net v0.0.0-20220615171555-694bf12d69de // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
)

@ -0,0 +1,161 @@
package goping
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"time"
)
// https://www.cnblogs.com/wlw-x/p/14169607.html
// https://www.cnblogs.com/wlw-x/p/14169607.html
//类型1个字节。8表示回显请求报文0表示回显响应报文。
//代码1个字节。回显请求报文、回显响应报文 时均为0。
//校验和2个字节。非重点略过。
//标识符2个字节。发送ICMP报文的客户端进程的id服务端会回传给客户端。因为同一个客户端可能同时运行多个ping程序这样客户端收到回西显报文可以知道是响应给哪个客户端进程的。
//序列号2个字节。从0开始客户端每次发送新的回显请求时+1。服务端原样会传。
//数据6个字节。客户端记录回显请求的发送时间服务端记录回西显响应的发送时间
// 分析:
//
// ping 命令在执行后显示出被测试系统主机名和相应的IP地址、返回给当前主机的ICMP报文顺序号、ttl生存时间和往返时间rtt(单位是毫秒)
//
// 要想真正了解ping 命令的实现原理首先要了解ping命令所以使用到的TCP/IP协议ICMP(Internet Control Message网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制使他们在遇到时尽可能的包错误报告发送给源发方。ICMP协议是IP层的一个协议但是由于差错报告在发送报文给报文源发方时可能也要经过若干子网因此涉及到路由选择等问题所以ICMP报文需要通过IP协议来发送。ICMP数据包的数据发送前需要进性两级封装
//
// 1. 首先添加ICMP报头形成ICMP报文
// 2. 在添加IP报头形成IP数据报
//
// 由于IP层协议是一种点对点的协议而非端对端的协议提供无连接的数据报服务没有端对端的概念因此很少使用bind() 和 connect() 函数若有使用也只是用于设置IP地址。
// 我们在go中定义 ICMP 的报文格式如下结构
const (
MAX_PG = 2000
)
// 封装 icmp 报头
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SequenceNum uint16
}
var (
originBytes []byte
)
func init() {
originBytes = make([]byte, MAX_PG)
}
func CheckSum(data []byte) (rt uint16) {
var (
sum uint32
length int = len(data)
index int
)
for length > 1 {
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length > 0 {
sum += uint32(data[index]) << 8
}
rt = uint16(sum) + uint16(sum>>16)
return ^rt
}
func Ping(domain string, PS, Count int) {
var (
icmp ICMP
laddr = net.IPAddr{IP: net.ParseIP("0.0.0.0")} // 得到本机的IP地址结构
raddr, _ = net.ResolveIPAddr("ip", domain) // 解析域名得到 IP 地址结构
max_lan, min_lan, avg_lan float64
)
// 返回一个 ip socket
conn, err := net.DialIP("ip4:icmp", &laddr, raddr)
if err != nil {
fmt.Println(err.Error())
return
}
defer conn.Close()
// 初始化 icmp 报文
icmp = ICMP{8, 0, 0, 0, 0}
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp)
//fmt.Println(buffer.Bytes())
binary.Write(&buffer, binary.BigEndian, originBytes[0:PS])
b := buffer.Bytes()
binary.BigEndian.PutUint16(b[2:], CheckSum(b))
//fmt.Println(b)
fmt.Printf("\nPING %s (%s) %d(%d) bytes of data.\n", domain, raddr.String(), PS, PS+28)
//fmt.Printf("\n正在 Ping %s 具有 %d(%d) 字节的数据:\n", raddr.String(), PS, PS+28)
recv := make([]byte, 1024)
ret_list := []float64{}
dropPack := 0.0 /*统计丢包的次数,用于计算丢包率*/
max_lan = 3000.0
min_lan = 0.0
avg_lan = 0.0
for i := Count; i > 0; i-- {
/*
++
*/
if _, err := conn.Write(buffer.Bytes()); err != nil {
dropPack++
time.Sleep(time.Second)
continue
}
// 否则记录当前得时间
t_start := time.Now()
conn.SetReadDeadline((time.Now().Add(time.Second * 3)))
len, err := conn.Read(recv)
/*
++
*/
if err != nil {
dropPack++
time.Sleep(time.Second)
continue
}
t_end := time.Now()
dur := float64(t_end.Sub(t_start).Nanoseconds()) / 1e6
ret_list = append(ret_list, dur)
if dur < max_lan {
max_lan = dur
}
if dur > min_lan {
min_lan = dur
}
fmt.Printf("%d bytes from %s (%s): icmp_seq=%d ttl=53 time=%.3f ms\n", len, raddr.String(), raddr.String(), Count-i+1, dur)
//fmt.Printf("来自 %s 的回复: 大小 = %d byte 时间 = %.3fms\n", raddr.String(), len, dur)
time.Sleep(time.Second)
}
fmt.Printf("--- %s ping statistics ---\n", raddr.String())
fmt.Printf("%d packets transmitted, %f lost, %.2f%% packet loss, time 1000ms\n", Count, dropPack, dropPack/float64(Count)*100)
if len(ret_list) == 0 {
avg_lan = 3000.0
} else {
sum := 0.0
for _, n := range ret_list {
sum += n
}
avg_lan = sum / float64(len(ret_list))
}
fmt.Printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f ms", min_lan, avg_lan, max_lan)
}

@ -0,0 +1,24 @@
package goping
import (
"testing"
)
func TestPing(tt *testing.T) {
//if len(os.Args) < 3 {
// fmt.Printf("Param domain |data package Sizeof|trace times\n Ex: ./Ping www.so.com 100 4\n")
// os.Exit(1)
//}
//PS, err := strconv.Atoi(os.Args[2])
//if err != nil {
// fmt.Println("you need input correct PackageSizeof(complete int)")
// os.Exit(1)
//}
//Count, err := strconv.Atoi(os.Args[3])
//if err != nil {
// fmt.Println("you need input correct Counts")
// os.Exit(1)
//}
Ping("www.baidu.com", 48, 5)
//Ping("www.baidu.com", 48, 5)
}

@ -0,0 +1,56 @@
package main
import (
"flag"
"fmt"
"os"
"pong/dial"
"pong/goping"
"strconv"
)
// 定义命令行参数对应的变量,这三个变量都是指针类型
var helpText = `usage: pong <adress> <port> [-t <timeout> -k <protocol>]
eg:
pong www.baidu.com
pong www.baidu.com 443
pong www.baidu.com 443 -t 1
pong www.baidu.com 443 -t 1 -k udp
pong -a www.baidu.com -p 443 -k tcp -t 3
`
func main() {
address := flag.String("a", "", "domain or ip address")
port := flag.Int("p", 0, "port")
protocol := flag.String("k", "tcp", "protocol kind")
timeout := flag.Int("t", 1, "timeout")
flag.Parse()
switch flag.NArg() { //函数返回没有被解析的命令行参数的个数
// flag.Args() 函数返回没有被解析的命令行参数
case 0:
if *address != "" && *port != 0 {
dial.PortDial(*address, strconv.Itoa(*port), *protocol, *timeout)
}
fmt.Printf(helpText)
os.Exit(0)
case 1:
if *port != 0 {
dial.PortDial(flag.Arg(0), strconv.Itoa(*port), *protocol, *timeout)
}
goping.Ping(flag.Arg(0), 48, 5)
os.Exit(0)
default:
fmt.Println("ping")
addressDial := flag.Arg(0)
portDial := flag.Arg(1)
if *address != "" {
addressDial = *address
}
if *port != 0 {
portDial = strconv.Itoa(*port)
}
dial.PortDial(addressDial, portDial, *protocol, *timeout)
}
}

@ -0,0 +1,34 @@
package main
//func TestPing(tt *testing.T) {
// p := fastping.NewPinger()
// ra, err := net.ResolveIPAddr("ip4:icmp", os.Args[1])
// if err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
// p.AddIPAddr(ra)
// p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
// fmt.Printf("IP Addr: %s receive, RTT: %v\n", addr.String(), rtt)
// }
// p.OnIdle = func() {
// fmt.Println("finish")
// }
// err = p.Run()
// if err != nil {
// fmt.Println(err)
// }
//}
//func TestPing(tt *testing.T) {
// cmd := exec.Command("ping", "www.google.com", "-c", "4", "-W", "5")
// fmt.Println("NetWorkStatus Start:", time.Now().Unix())
// err := cmd.Run()
// fmt.Println("NetWorkStatus End :", time.Now().Unix())
// if err != nil {
// fmt.Println(err.Error())
// } else {
// fmt.Println("Net Status , OK")
// }
//
//}

@ -0,0 +1,6 @@
## extend tool for ping
pong www.baidu.com
pong www.baidu.com 443
pong www.baidu.com 443 -t 1
pong www.baidu.com 443 -t 1 -k udp
pong -a www.baidu.com -p 443 -k tcp -t 3
Loading…
Cancel
Save