1

主に楽しみのために、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)
}

ライブラリは特定の用途のために作成されたものではありません。いくつかのプロジェクトを実行するために使用しますが、各選択肢の引数を知りたいです。

そして、私が見たように、2番目のオプションを実装するのははるかに困難です.

ありがとう!(私の投稿が明確でない場合は、遠慮なく明確にするように依頼してください。遅いです。)

プロジェクトアドレスを確認したい方はこちら

4

2 に答える 2

1

あなたの質問は次のようなものだと理解しました:「既にタイムアウトとして報告したパケットをユーザーに報告すべきですか?

いいえ、私はこれをしません。アプリケーションでは、パケットが 2 回必要になることはなく、手動でこれらの簿記を行う必要があります。あなたの図書館が簿記を行っており、パケットが後で受信されたかどうかを後で尋ねることができる場合、それは問題ありません.

したがって、いいえ、または次のような API のいずれかです。

notifyReceivedLostPacket(seqId int) chan Packet
于 2013-11-03T03:10:33.377 に答える
0

私は反対票を投じます:

はい、これを行う必要があります。返信がありました。それは報告されるべきです。アプリケーションは、それをどうするかを理解する必要があります。

聞く

于 2013-11-04T05:08:36.733 に答える