Go에서 핑 만들기

15987 단어 gopingnetwork
다른 날에는 학습 목적으로 Ping in Go 프로그래밍 언어를 만듭니다.

솔직히 말해서 Ping이 어떻게 작동하는지 깊이 알고 싶어서 외부 패키지에 의존하지 않고 만들고 싶었습니다.
그러나 다행스럽게도(그리고 불행하게도) Go에서 ICMP 패킷을 전송하기 위한 유용한 패키지(예: "net/icmp", "net/ipv4"또는 "net/ipv6")가 있습니다. 그래서 나는 그것들을 사용하기로 결정했습니다.

이 코드repository는 여기에 있는 코드의 향상된 버전입니다. 관심있으신 분들은 꼭 확인해주세요.

1. 새 Go 프로젝트 만들기



먼저 새 폴더를 만들고 "go mod init"명령을 사용하여 새 프로젝트를 만듭니다.
그런 다음 새 파일을 만듭니다. "메인.고".

mkdir myping
cd myping
go mod init example/myping
touch main.go



2. 외부 패키지 가져오기



좋아하는 코드 편집기를 열고 "main.go"에서 코드 작성을 시작하세요.
ICMP 패킷 수신/전송에 사용되는 일부 패키지와 결과 표시를 위한 일반적인 패키지를 가져옵니다.

package main

import (
    "fmt"
    "log"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "golang.org/x/net/ipv6"
)



3. ICMP 패킷 수신기 만들기



패킷이 목적지에서 돌아오는지 확인하려면 리스너를 만들고 패킷을 받을 때까지 기다려야 합니다.
여기에서는 네트워크 구조에 IPv4를 사용하므로 "icmp.ListenPacket"인수의 네트워크 값은 "ip4:icmp"또는 "ip4:1"입니다. IPv6을 사용하는 경우 이 값은 "ip6:ipv6-icmp"또는 "ip6:58"이어야 합니다.

package main

...

func main() {

    packetconn, err := icmp.ListenPacket("ip4:1", "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer packetconn.Close()

}



4. ICMP 메시지 준비



다음으로 코드, 식별자, 데이터 등을 포함하는 ICMP 메시지를 만듭니다.
위에서 말했듯이 저는 Ipv4를 사용하므로 Type 필드를 ipv4.ICMPType으로 설정합니다.
ping에 대한 코드 필드에 0(에코 응답)을 설정합니다.

...

func main() {

    ...

    msg := &icmp.Message{
        Type: ipv4.ICMPType,
        Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff,
            Seq: 0,
            Data: []byte("hello"),
        }
    }

    wb, err := msg.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }
}



5. 패킷 전송 구현



WriteTo 함수는 ICMP 메시지를 대상에 씁니다.
이번에는 예로 Google DNS(8.8.8.8)를 대상으로 설정합니다.

...

func main() {

    ...

    if _, err := packetconn.WriteTo(wb, "8.8.8.8"); err != nil {
        log.Fatal(err)
    }
}



6. 메시지 수신 구현



목적지로부터의 패킷은 여기에서 처리됩니다.

...

func main() {

    ...

    rb := make([]byte, 1500)
    n, peer, err := packetconn.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }

    rm, err != icmp.ParseMessage(1, rb[:n])
    if err !=nil {
        log.Fatal(err)
    }
}



7. 결과를 표시하도록 구현



마지막에 받은 패킷 또는 실패한 정보를 표시합니다.

...

func main() {

    ...

    switch rm.Type {
        case ipv4.ICMPTypeEchoReply:
            fmt.Printf("received from %v", peer)
        default:
            fmt.Printf("Failed: %+v\n", rm)
    }
}



빌드 및 실행



구현 후 ping이 올바르게 작동하는지 확인하십시오.

go mod tidy
go build
./myping



결론



위와 같이 유용한 패키지를 사용하면 ICMP 패킷을 너무 쉽게 보낼 수 있습니다.
아래는 전체 코드입니다.

package main

import (
    "fmt"
    "log"

    "golang.org/x/net/icmp"
    "golang.org/x/net/ipv4"
    "golang.org/x/net/ipv6"
)

func main() {
    packetconn, err := icmp.ListenPacket("ip4:1", "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer packetconn.Close()

    msg := &icmp.Message{
        Type: ipv4.ICMPType,
        Code: 0,
        Body: &icmp.Echo{
            ID: os.Getpid() & 0xffff,
            Seq: 0,
            Data: []byte("hello"),
        }
    }

    wb, err := msg.Marshal(nil)
    if err != nil {
        log.Fatal(err)
    }

    if _, err := packetconn.WriteTo(wb, "8.8.8.8"); err != nil {
        log.Fatal(err)
    }

    rb := make([]byte, 1500)
    n, peer, err := packetconn.ReadFrom(rb)
    if err != nil {
        log.Fatal(err)
    }

    rm, err != icmp.ParseMessage(1, rb[:n])
    if err !=nil {
        log.Fatal(err)
    }

    switch rm.Type {
        case ipv4.ICMPTypeEchoReply:
            fmt.Printf("received from %v", peer)
        default:
            fmt.Printf("Failed: %+v\n", rm)
    }
}


사실 내부 부품들을 더 살펴보면서 만들어보고 싶었는데 패키지들이 너무 유용해서 깊이 따라하지는 못했어요. 나중에 기분이 좋으면 다시 시도하고 싶습니다.

좋은 웹페이지 즐겨찾기