2013-11-03 57 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