2013-11-03 63 views
1

我正在制作一个ping库,主要是为了好玩。制作ping库。我应该遵循实际的ping行为吗?

我最近在我的实现中发现了一个错误,我没有检查收到的打包seq。如果发生超时,我现在通过丢弃数据包来修复它。

但今天,我看到ping实用程序打印收到的回复数据包,即使它们超时。

Request timeout for icmp_seq 2 
Request timeout for icmp_seq 3 
64 bytes from 80.67.169.18: icmp_seq=2 ttl=58 time=2216.104 ms 
64 bytes from 80.67.169.18: icmp_seq=3 ttl=58 time=1216.559 ms 

我不知道我的图书馆应该做什么。我是否应该保持实际行为,还是需要将其调整为“旧”ping方式?

/* 
Package libping provide the ability to send ICMP packets easily. 
*/ 
package libping 

import (
    "bytes" 
    "net" 
    "os" 
    "time" 
) 

const (
    ICMP_ECHO_REQUEST = 8 
    ICMP_ECHO_REPLY = 0 
) 

// The struct Response is the data returned by Pinguntil. 
type Response struct { 
    Delay  time.Duration 
    Error  error 
    Destination string 
    Seq   int 
    Readsize int 
    Writesize int 
} 

func makePingRequest(id, seq, pktlen int, filler []byte) []byte { 
    p := make([]byte, pktlen) 
    copy(p[8:], bytes.Repeat(filler, (pktlen-8)/len(filler)+1)) 

    p[0] = ICMP_ECHO_REQUEST // type 
    p[1] = 0     // code 
    p[2] = 0     // cksum 
    p[3] = 0     // cksum 
    p[4] = uint8(id >> 8) // id 
    p[5] = uint8(id & 0xff) // id 
    p[6] = uint8(seq >> 8) // sequence 
    p[7] = uint8(seq & 0xff) // sequence 

    // calculate icmp checksum 
    cklen := len(p) 
    s := uint32(0) 
    for i := 0; i < (cklen - 1); i += 2 { 
     s += uint32(p[i+1])<<8 | uint32(p[i]) 
    } 
    if cklen&1 == 1 { 
     s += uint32(p[cklen-1]) 
    } 
    s = (s >> 16) + (s & 0xffff) 
    s = s + (s >> 16) 

    // place checksum back in header; using ^= avoids the 
    // assumption the checksum bytes are zero 
    p[2] ^= uint8(^s & 0xff) 
    p[3] ^= uint8(^s >> 8) 

    return p 
} 

func parsePingReply(p []byte) (id, seq, code int) { 
    id = int(p[24])<<8 | int(p[25]) 
    seq = int(p[26])<<8 | int(p[27]) 
    code = int(p[21]) 
    return 
} 

// Pingonce send one ICMP echo packet to the destination, and return the latency. 
// The function is made to be simple. Simple request, simple reply. 
func Pingonce(destination string) (time.Duration, error) { 
    response := make(chan Response) 
    go Pinguntil(destination, 1, response, time.Second) 
    answer := <-response 
    return answer.Delay, answer.Error 
} 

// Pinguntil will send ICMP echo packets to the destination until the counter is reached, or forever if the counter is set to 0. 
// The replies are given in the Response format. 
// You can also adjust the delay between two ICMP echo packets with the variable delay. 
func Pinguntil(destination string, count int, response chan Response, delay time.Duration) { 
    raddr, err := net.ResolveIPAddr("ip", destination) 
    if err != nil { 
     response <- Response{Delay: 0, Error: err, Destination: destination, Seq: 0} 
     close(response) 
     return 
    } 

    ipconn, err := net.Dial("ip:icmp", raddr.IP.String()) 
    if err != nil { 
     response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: 0} 
     close(response) 
     return 
    } 

    sendid := os.Getpid() & 0xffff 
    pingpktlen := 64 
    seq := 0 
    var elapsed time.Duration = 0 

    for ; seq < count || count == 0; seq++ { 
     elapsed = 0 
     if seq > 65535 { // The two bytes for seq. Don't overflow! 
      seq = 0 
     } 
     sendpkt := makePingRequest(sendid, seq, pingpktlen, []byte("Go Ping")) 

     start := time.Now() 

     writesize, err := ipconn.Write(sendpkt) 
     if err != nil || writesize != pingpktlen { 
      response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: 0} 
      time.Sleep(delay) 
      continue 
     } 

     ipconn.SetReadDeadline(time.Now().Add(time.Second * 1)) // 1 second 

     resp := make([]byte, 1024) 
     for { 
      readsize, err := ipconn.Read(resp) 

      elapsed = time.Now().Sub(start) 

      rid, rseq, rcode := parsePingReply(resp) 

      if err != nil { 
       response <- Response{Delay: 0, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: readsize} 
       break 
      } else if rcode != ICMP_ECHO_REPLY || rseq != seq || rid != sendid { 
       continue 
      } else { 
       response <- Response{Delay: elapsed, Error: err, Destination: raddr.IP.String(), Seq: seq, Writesize: writesize, Readsize: readsize} 
       break 
      } 
     } 
     time.Sleep(delay - elapsed) 
    } 
    close(response) 
} 

该库不是用于specitif用法。我会用它来做一些项目,但我想知道每个选择的参数。

而我看,执行第二个选项将会更困难。

谢谢! (如果我的职位是不明确的,不要犹豫,问我要澄清,这是晚了。)

如果你想检查项目地址:here

回答

1

我明白你的问题是:“应该我向用户报告已报告超时的数据包'。

没有,我不会这样做。在一个应用程序中,我不会期望数据包两次,我将不得不为这些手动进行簿记。如果你的图书馆做了簿记工作,并且我可以在稍后的时间询问是否稍后再收到此包,那就没问题。

所以没有或一些API是这样的:

notifyReceivedLostPacket(seqId int) chan Packet 
+0

我实际上报告超时。我的问题是“我应该报告所有收到的数据包,即使我将它们报告为超时?” – Cubox

+1

是的,否则你会有两种机制将信息传递给用户:1.这是一个数据包; 2.这是一个超时。有一个统一的方式更好:这是一个数据包,它还没有耗尽时间。 – nemo

+0

是的,但重点在于用户将收到“数据包2已超时!”并且在“我们收到了包2!”之后不久 – Cubox

0

我会做出相反的表决方式:

,你应该这样做。我们得到了回应;应该报告。应用程序将不得不弄清楚如何处理它。

询问

+0

你的应用程序告诉ping库什么时候一个数据包应该被视为丢失。我会报告一个已经丢失的数据包,因为错误或者至少是意外的行为。这就是为什么我说不或只是明确地告诉图书馆这样做。 – nemo