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. 完全な例完全なコードは次のとおりです:
パッケージ mainimport (
"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 サイトの他の関連記事を参照してください。