The method of simple ping process with go language

Time:2020-5-11

1、 Preparations

Install the latest go

1. If there is no VPN due to Google being blocked, download it here: http://www.golangtc.com/download

2. Using any text editor or liteide will make compilation and debugging easier

2、 Coding

Package to use:


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

1. Using the related functions in the net package provided by golang, we can quickly construct an IP package and customize some of its key parameters without manually filling in the IP packets.

2. Using encoding / binary package, you can easily get the memory data of struct and specify byte order (in this case, you need to use network byte order bigendian), without having to convert byte order yourself. In the previous article, when using boost, you have to implement the conversion process yourself

3. Use container / list package to facilitate result statistics

4. Time consuming and timeout processing with time package

ICMP message struct:


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

Usage tips:


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

Note that the ping program, including the previous ARP program, must be executed with the highest authority of the system, so here is a prompt to use thetime.Sleep(5e9), pause for 5 seconds, to make the double-click performer see the prompt and avoid the console flash.

Creation and initialization of key net objects:


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.DialIPIt means to generate an IP message, the version number is V4, and the protocol is ICMP (here stringip4:icmpThe protocol field of IP message will be set to 1 to indicate ICMP Protocol),

The source address LADDR can be 0.0.0.0 or its own IP address, which does not affect the work of ICMP.

The destination address, raddr, is a URL. Here, resolve is used for DNS resolution. Note that the return value is a pointer, so the parameter in the dialip method below indicates that there is no address character.

Such a complete IP message is assembled. We don’t worry about other fields in IP, and go has handled them for us.

Returned byconn *net.IPConnObject can be used for subsequent operations.

defer conn.Close() Indicates that the function willReturnIs executed to ensure that the shutdown is not forgotten.

The ICMP message needs to be constructed as follows:


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)

It is still very simple. With binary, you can read the data of a structure into the buffer according to the specified byte order, calculate the check sum, and then read it.

Check and algorithm refer to the implementation in the URL given above:


func CheckSum(data []byte) 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])
 }
 sum += (sum >> 16)
 return uint16(^sum)
}

The following is the Ping request process, which is modeled on the windows ping process. By default, it only takes 4 times:

FMT. Printf ("\ nping% s with 0 bytes of data: \ n", raddr. String())
 recv := make([]byte, 1024)
 statistic := list.New()
 sended_packets := 0
 for i := 4; i > 0; i-- {
 if _, err := conn.Write(buffer.Bytes()); err != nil {
  fmt.Println(err.Error())
  return
 }
 sended_packets++
 t_start := time.Now()
 conn.SetReadDeadline((time.Now().Add(time.Second * 5)))
 _, err := conn.Read(recv)
 if err != nil {
  Fmt.println ("request timeout")
  continue
 }
 t_end := time.Now()
 dur := t_end.Sub(t_start).Nanoseconds() / 1e6
 FMT. Printf ("reply from% s: time =% DMS \ n", raddr. String(), DUR)
 statistic.PushBack(dur)
 //for i := 0; i < recvsize; i++ {
 // if i%16 == 0 {
 // fmt.Println("")
 // }
 // fmt.Printf("%.2x ", recv[i])
 //}
 //fmt.Println("")
 }

“Data with 0 bytes” means that there is no data field in ICMP message, which is slightly different from the 32 bytes data in windows.

conn.WriteAfter the method is executed, an ICMP request is sent, with timing and counting.

conn.SetReadDeadlineYou can stop the read wait for a specified period of time without receiving data, return an error err, and then determine the request timeout. Otherwise, after receiving the response, calculate the time spent back and forth, and put it into a list for subsequent statistics.

The comment part is the code when I explore the returned data. Readers can try to see which data package the read data belongs to?

Statistical work will be done at the end of the loop. Defer is used here, but it is hoped that return can be executed after Ctrl+C is pressed, but the console is not really capable of being suck directly.

defer func() {
 fmt.Println("")
 //Information statistics
 var min, max, sum int64
 if statistic.Len() == 0 {
  min, max, sum = 0, 0, 0
 } else {
  min, max, sum = statistic.Front().Value.(int64), statistic.Front().Value.(int64), int64(0)
 }
 for v := statistic.Front(); v != nil; v = v.Next() {
  val := v.Value.(int64)
  switch {
  case val < min:
  min = val
  case val > max:
  max = val
  }
  sum = sum + val
 }
 recved, losted := statistic.Len(), sended_packets-statistic.Len()
 Fmt.printf (pingstatistics for% s: \ npackets: sent =% D, received =% D, lost =% d (%. 1F%% lost), \ nexpective time of round trip (in milliseconds): \ nminimum =% DMS, maximum =% DMS, average =%. 0fms \ n ",
  raddr.String(),
  sended_packets, recved, losted, float32(losted)/float32(sended_packets)*100,
  min, max, float32(sum)/float32(recved),
 )
 }()

Note the conversion and formatting of types in the statistical process.

This is all the code. The execution result looks like this:

 

Note that there is no “break” after each Ping. Unlike windows or Linux, it will pause for a few seconds before the next round of Ping.

summary

Golang’s implementation of the whole Ping is much simpler than I thought. The static compilation speed is very fast. Compared with C, you need to know more about the underlying layer, and even start from the link layer. You need to write more and more complex code to complete the same work. But at the root, C language is still the ancestor, and its function is indispensable. Many principles and ideas should be inherited and developed Golang did a good job. The above is the whole content of this article, hoping to bring some help to your study or work. If you have any questions, you can leave a message for communication.

Recommended Today

Redis (1)

Redis About redis Remote dictionary server (redis) is a key value storage system. Redis is an open source log type, key value database written in ANSI C language, complying with BSD protocol, supporting network, memory based and persistent, and providing API in multiple languages. It is often referred to as a data structure server, because […]