ホームページ  >  記事  >  バックエンド開発  >  golangでpingを実装する方法

golangでpingを実装する方法

PHPz
PHPzオリジナル
2023-04-25 10:42:381599ブラウズ

golang は ping を実装します

Ping はネットワーク接続のテストに使用されるツールで、コンピューター ネットワークの接続性をテストするために使用されます。 Ping コマンドは、ターゲット ホストがオンラインか、アクセス可能か、および通信遅延をテストできます。この記事では、プログラミング言語 golang を使用して ping ツールを実装する方法を紹介します。

1. Ping とは

Ping は、プログラマーやシステム管理者がネットワークのデバッグやトラブルシューティングを行うために不可欠なツールです。 Ping は Packet Internet Groper の略語で、その機能は 2 つのホスト間のネットワーク接続の接続性をテストすることです。 Ping コマンドは、インターネット上の最も基本的なネットワーク サービスの 1 つで、ICMP パケットをターゲット ホストに送信し、応答時間を測定することで、接続のリアルタイム性と信頼性をチェックします。

2. Ping の仕組み

Ping の原理は、インターネット制御メッセージ プロトコル (ICMP) の「echo request」コマンドを使用して、特別なデータ パケットをターゲット ホストに送信することです。このパケットを受信すると、自動的に「エコー応答」パケットが送信者に返され、送信者はパケットの往復時間を測定することで、対象ホストの応答時間やネットワーク遅延を計算することができます。

3. ping の実装

ping を実装するには、次の 3 つの手順を完了する必要があります:

1. ICMP データ パケットの構築
2. ICMP データ パケットの送信
3 .ICMP パケットによって返された応答情報を解析します

1.ICMP パケットを構築します

まず、ICMP パケットを構築する必要があります。 ICMP フォーマットに従って、Go 言語に付属する「icmp」パッケージによって提供される構造と関数を使用して、ICMP データ パケットを構築できます。 icmp.Message と icmp.Echo はパケット タイプとタイプ関連データを指定し、icmp.Marshal は構築した ICMP データを取得します。 PingMsg は次のように定義できます:

type PingMsg struct{

icmpMessage icmp.Message
bytes []byte

}

func newEchoPingMessage(id, seq int) (*PingMsg, error){

bytes := make([]byte, 32)

//ICMP パケットの内容を埋める

for i, _ := range bytes {
    bytes[i] = 'a' + byte(i%26)
}
icmpMessage := icmp.Message{
    Type: ipv4.ICMPTypeEcho, // ICMP类型
    Code: 0, // ICMP代码,置为0
    Checksum: 0, // 校验和,暂时置为0
    Body: &icmp.Echo{
        ID: id,
        Seq: seq,
        Data: bytes,
    },
}

//ICMP パケットをシリアル化する

b, err := icmpMessage.Marshal(nil)
if err != nil {
    return nil, err
}
return &PingMsg{
    bytes: b,
    icmpMessage: icmpMessage,
}, nil

}

2. ICMP パケットを送信する

#次に、構築された ICMP パケットを送信するには、net パッケージによって提供される Conn インターフェイスを使用する必要があります。 net.Dial("ip4:icmp", destination) を使用して ICMP 接続を確立し、Conn.Write(b []byte) を使用してデータ パケットをターゲット ホストに送信し、time.NewTicker 関数を使用して送信時間間隔。

func ping(addr string) error{

conn, err := net.Dial("ip4:icmp", addr)
if err != nil {
    return err
}
defer conn.Close()
pingMsg, err := newEchoPingMessage(os.Getpid()&0xffff, 1)
if err != nil {
    return err
}
timeout := 5 * time.Second // 超时时间
ticker := time.NewTicker(time.Second) //设置1秒钟的时间间隔
defer ticker.Stop()
for {
    select {
    case <- ticker.C: // 每1秒钟发送一次ICMP数据包
        if _, err = conn.Write(pingMsg.bytes); err != nil {
            return err
        }
    case <- time.After(timeout): // 超时时间到了,抛出错误
        return errors.New("request timeout")
    }
}
}

3. ICMP パケットによって返された応答情報を解析します

最後に、次のことを行う必要があります。 read 返された ICMP パケットを取得し、ホストの応答情報を解析します。また、net パッケージによって提供される Conn インターフェイスを使用して、返されたデータ パケットを読み取り、icmp.ParseMessage を使用してデータ パケットを解析し、icmp.Message.Body.(*icmp.EchoReply) を通じて応答情報を取得します。

func acceptPing(conn net.Conn) エラー{

for {
    b := make([]byte, 512)
    conn.SetReadDeadline(time.Now().Add(time.Second)) // 设置超时时间
    if n, err := conn.Read(b); err != nil {
        if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
            continue
        }
        return err
    } else {
        recvMsg, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply, b[:n])
        if err != nil {
            return err
        }
        switch recvMsg.Type {
        case ipv4.ICMPTypeEchoReply:
            echoReply, _ := recvMsg.Body.(*icmp.EchoReply)
            log.Printf("ping %d bytes from %s: icmp_seq=%d time=%dms", len(echoReply.Data), conn.RemoteAddr().String(), echoReply.Seq, time.Since(time.Unix(0, echoReply.ArrivalTime().Nanoseconds())).Nanoseconds())
        default:
            log.Printf("unsupported ICMP message type %v (%v)", recvMsg.Type, recvMsg.Code)
        }
    }
}
}

4. 完全な例

完全なコードは次のとおりです:

パッケージ main

import (

"errors"
"fmt"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"log"
"net"
"os"
"time"
)

type PingMsg struct{

icmpMessage icmp.Message
bytes       []byte
}

func newEchoPingMessage(id, seq int) (*PingMsg, エラー){

bytes := make([]byte, 32)
for i, _ := range bytes {
    bytes[i] = 'a' + byte(i%26)
}
icmpMessage := icmp.Message{
    Type: ipv4.ICMPTypeEcho,
    Code: 0,
    Checksum: 0,
    Body: &icmp.Echo{
        ID: id,
        Seq: seq,
        Data: bytes,
    },
}
b, err := icmpMessage.Marshal(nil)
if err != nil {
    return nil, err
}
return &PingMsg{
    bytes: b,
    icmpMessage: icmpMessage,
}, nil
}

func ping(addr string) エラー{

conn, err := net.Dial("ip4:icmp", addr)
if err != nil {
    return err
}
defer conn.Close()
pingMsg, err := newEchoPingMessage(os.Getpid()&0xffff, 1)
if err != nil {
    return err
}
timeout := 5 * time.Second
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
    select {
    case <- ticker.C:
        if _, err = conn.Write(pingMsg.bytes); err != nil {
            return err
        }
    case <- time.After(timeout):
        return errors.New("request timeout")
    }
}
}

func acceptPing(conn net.Conn ) error{

for {
    b := make([]byte, 512)
    conn.SetReadDeadline(time.Now().Add(time.Second))
    if n, err := conn.Read(b); err != nil {
        if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
            continue
        }
        return err
    } else {
        recvMsg, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply, b[:n])
        if err != nil {
            return err
        }
        switch recvMsg.Type {
        case ipv4.ICMPTypeEchoReply:
            echoReply, _ := recvMsg.Body.(*icmp.EchoReply)
            log.Printf("ping %d bytes from %s: icmp_seq=%d time=%dms", len(echoReply.Data), conn.RemoteAddr().String(), echoReply.Seq, time.Since(time.Unix(0, echoReply.ArrivalTime().Nanoseconds())).Nanoseconds())
        default:
            log.Printf("unsupported ICMP message type %v (%v)", recvMsg.Type, recvMsg.Code)
        }
    }
}
}

func main(){

if err := ping("www.baidu.com"); err != nil{
    log.Fatal(err)
}
}

このコードが実行されると、ICMP データが送信されます。継続的 パケットは、ホストがタイムアウトするか応答を受信するまで、ターゲット ホストに送信されます。応答パケットが受信されるたびに、プログラムは次の情報を出力します:

ping 32 bytes from 120.92.6.25: icmp_seq=0 time=29ms

これは、応答パケットが正常に受信されたことを意味します。そして復路の所要時間が表示されます。 Ping の実装プロセスは非常に単純ですが、ネットワーク通信の原理をより深く理解できるようになります。

以上がgolangでpingを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。