宅男在线永久免费观看网直播,亚洲欧洲日产国码无码久久99,野花社区在线观看视频,亚洲人交乣女bbw,一本一本久久a久久精品综合不卡

全部
常見問題
產品動態(tài)
精選推薦

用Go實現Ping操作

管理 管理 編輯 刪除


Ping 是什么?

? ping 是一個計算機網絡工具,通常用于測試網絡連接的可達性和測量往返時間。在大多數操作系統中,ping 命令是一個內置的命令行工具,可以通過命令行終端使用。例如,在 Windows 操作系統中,你可以在命令提示符中運行 ping 命令,而在類 Unix 操作系統(如 Linux 和 macOS)中,你可以在終端中使用 ping 命令。通常,命令的語法是 ping 目標主機或 IP,然后命令將輸出與目標主機的通信狀態(tài)和 RTT 相關的信息。

Ping 有什么用處?

? Ping 工具主要有以下幾個主要用途:

  1. 測試主機的可達性ping 命令用于檢查另一個主機是否可以在網絡上訪問。它向目標主機發(fā)送一個小的數據包(通常是 ICMP Echo Request),如果目標主機正常工作,它將響應一個回復數據包(通常是 ICMP Echo Reply)。如果沒有響應,那么目標主機可能無法訪問或處于離線狀態(tài)。
  2. 測量往返時間(RTT)ping 命令通常會顯示每次請求和響應之間的時間差,這被稱為往返時間(RTT)。這個值表示了數據從發(fā)送端到接收端的往返延遲,通常以毫秒為單位。測量 RTT 對于評估網絡性能和延遲非常有用。
  3. 網絡故障排除ping 是網絡故障排除的有用工具之一。通過檢查 ping 的輸出,網絡管理員可以確定網絡連接是否正常,以及延遲是否在可接受范圍內。如果 ping 失敗,管理員可以進一步調查網絡故障的原因。
  4. 監(jiān)測網絡穩(wěn)定性ping 命令還可以用于監(jiān)測網絡的穩(wěn)定性。通過連續(xù)地向目標主機發(fā)送 ping 請求,可以了解網絡連接的質量和穩(wěn)定性。如果出現不穩(wěn)定性,管理員可以及時采取措施。

動手實現一個 Ping 工具

? 首先,我們要了解一下 Ping 操作的工作原理:向網絡上的另一個主機系統發(fā)送 ICMP 報文,如果指定系統得到了報文,它將把回復報文傳回給發(fā)送者。

? ICMP 報文由 ICMP 報文頭 和 數據包組成,其報文頭包含 Type、Code、Checksum、ID、SequenceNum 字段。因此,我們需要先在本地主機上定義 ICMP 請求報文結構體:

type ICMP struct {
    Type        uint8  // 類型
    Code        uint8  // 代碼
    CheckSum    uint16 // 校驗和
    ID          uint16 // ID
    SequenceNum uint16 // 序號
}

? 上面只是 ICMP 的報文頭,我們在后面還需要為這個報文構建請求數據。需要注意的是,定義的順序不能亂,因為我們發(fā)送數據包是按字節(jié)發(fā)送的,所以獲取對應的字段的時候,也是按照對應字段的位置去獲取的,如果順序亂了,獲取到的數據就會出錯。

? 在構建數據之前,我們先設置好命令行參數,以獲取對應參數和目標 IP,同時需要定義全局變量,將命令行參數綁定到對應的變量中,方便使用:

var (
    helpFlag bool
    timeout  int64 // 耗時
    size     int   // 大小
    count    int   // 請求次數
)

func GetCommandArgs() {
    flag.Int64Var(&timeout, "w", 1000, "請求超時時間")
    flag.IntVar(&size, "l", 32, "發(fā)送字節(jié)數")
    flag.IntVar(&count, "n", 4, "請求次數")
    flag.BoolVar(&helpFlag, "h", false, "顯示幫助信息")
    flag.Parse()
}

? 在 main 函數中,啟用命令行參數設置:

func main() {
    GetCommandArgs()
}

? 在發(fā)送報文前,我們需要先建立連接,此時需要先獲取目標 IP,這個由命令行參數中獲?。?/p>

// 獲取目標 IP
desIP := os.Args[len(os.Args)-1]
// 構建連接
conn, err := net.DialTimeout("ip:icmp", desIP, time.Duration(timeout)*time.Millisecond)
if err != nil {
    log.Println(err.Error())
    return
}
defer conn.Close()
// 遠程地址
remoteaddr := conn.RemoteAddr()

? 連接建立后,我們需要根據參數中的發(fā)送次數 count 去發(fā)送對應次數的報文,因此需要用 for 去做:

for i := 0; i < count; i ++ {
    ...
}

? 同樣,我們在全局變量中添加對應的值:

var (
    typ      uint8 = 8
    code     uint8 = 0
)

? 做好前面的準備工作,我們就可以開始構建我們的 ICMP 請求報文了。我們這里以發(fā)送的第幾次作為 ID 和序列號:

icmp := &ICMP{
        Type:        typ,
        Code:        code,
        CheckSum:    uint16(0),
        ID:          uint16(i),
        SequenceNum: uint16(i),
    }

? 由于 ICMP 是使用二進制進行傳輸的,所以我們需要將信息用二進制表示:

var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp)

? 然后根據發(fā)送數據的大小 size 構建數據并寫在 ICMP 報文后面:

data := make([]byte, size)
buffer.Write(data)
data = buffer.Bytes()

? 現在,就只差一個校驗和字段了,計算 ICMP(Internet Control Message Protocol)報文的校驗和字段遵循以下步驟:

  1. 將報文分為 16 位的字(兩個字節(jié))。
  2. 對所有字進行按位求和(二進制求和),包括數據部分和報文頭。如果有剩余字節(jié)(奇數個字節(jié)),將其附加到最后一個字節(jié)。
  3. 將溢出的進位位(如果有)加回到結果中。
  4. 取結果的二進制反碼(按位取反)

? 代碼實現如下:

func checkSum(data []byte) uint16 {
    // 第一步:兩兩拼接并求和
    length := len(data)
    index := 0
    var sum uint32
    for length > 1 {
        // 拼接且求和
        sum += uint32(data[index])<<8 + uint32(data[index+1])
        length -= 2
        index += 2
    }
    // 奇數情況,還剩下一個,直接求和過去
    if length == 1 {
        sum += uint32(data[index])
    }

    // 第二部:高 16 位,低 16 位 相加,直至高 16 位為 0
    hi := sum >> 16
    for hi != 0 {
        sum = hi + uint32(uint16(sum))
        hi = sum >> 16
    }
    // 返回 sum 值 取反
    return uint16(^sum)
}

? 接著再將算出來的校驗和放到報文頭對應的位置中去,這里需要計算一下位置。假設我們有以下 ICMP 報文:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Type       |      Code       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Checksum (2 bytes)       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Identifier (2 bytes)   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Sequence Number (2 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Data (variable length) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

? 校驗和屬于報文的第3、4個字節(jié),即 data[2] 和 data[3]。

data[2] = byte(checkSum >> 8)
data[3] = byte(checkSum)

? 最后再設置一下超時時間,就可以將數據 data 寫入連接中了:

// 設置超時時間
conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond))

// 將 data 寫入連接中,
n, err := conn.Write(data)
if err != nil {
    log.Println(err)
    continue
}

? 發(fā)送完成后,再構建緩沖接收響應包,

buf := make([]byte, 1024)
n, err = conn.Read(buf)
//fmt.Println(data)
if err != nil {
    log.Println(err)
    continue
}

? 然后我們就可以從響應包中獲取我們需要的數據,比如 IP 地址、TTL等:

? 根據抓到的 ICMP 響應包,可以知道 IP 頭共 20 個字節(jié),源 IP 和 目標 IP 在我們接收的數據包的倒數 8 個字節(jié)里,所以我們可以推算出我們訪問的 IP 地址,就可以構建我們的打印信息了:

fmt.Printf("來自 %d.%d.%d.%d 的回復:字節(jié)=%d 時間=%d TTL=%d\n", buf[12], buf[13], buf[14], buf[15], n-28, t, buf[8])

? 至此,我們 Ping 工具的核心功能就實現了,還有一些統計信息,就不做具體的講解了,感興趣的可以從代碼中看具體的實現。

完整代碼如下:

package main

import (
    "bytes"
    "encoding/binary"
    "flag"
    "fmt"
    "log"
    "math"
    "net"
    "os"
    "time"
)

// tcp 報文前20個是報文頭,后面的才是 ICMP 的內容。
// ICMP:組建 ICMP 首部(8 字節(jié)) + 我們要傳輸的內容
// ICMP 首部:type、code、校驗和、ID、序號,1 1 2 2 2
// 回顯應答:type = 0,code = 0
// 回顯請求:type = 8, code = 0

var (
    helpFlag bool
    timeout  int64 // 耗時
    size     int   // 大小
    count    int   // 請求次數
    typ      uint8 = 8
    code     uint8 = 0
    SendCnt  int                   // 發(fā)送次數
    RecCnt   int                   // 接收次數
    MaxTime  int64 = math.MinInt64 // 最大耗時
    MinTime  int64 = math.MaxInt64 // 最短耗時
    SumTime  int64                 // 總計耗時
)

// ICMP 序號不能亂
type ICMP struct {
    Type        uint8  // 類型
    Code        uint8  // 代碼
    CheckSum    uint16 // 校驗和
    ID          uint16 // ID
    SequenceNum uint16 // 序號
}

func main() {
    fmt.Println()
    log.SetFlags(log.Llongfile)
    GetCommandArgs()

    // 打印幫助信息
    if helpFlag {
        displayHelp()
        os.Exit(0)
    }

    // 獲取目標 IP
    desIP := os.Args[len(os.Args)-1]
    //fmt.Println(desIP)
    // 構建連接
    conn, err := net.DialTimeout("ip:icmp", desIP, time.Duration(timeout)*time.Millisecond)
    if err != nil {
        log.Println(err.Error())
        return
    }
    defer conn.Close()
    // 遠程地址
    remoteaddr := conn.RemoteAddr()
    fmt.Printf("正在 Ping %s [%s] 具有 %d 字節(jié)的數據:\n", desIP, remoteaddr, size)
    for i := 0; i < count; i++ {
        // 構建請求
        icmp := &ICMP{
            Type:        typ,
            Code:        code,
            CheckSum:    uint16(0),
            ID:          uint16(i),
            SequenceNum: uint16(i),
        }

        // 將請求轉為二進制流
        var buffer bytes.Buffer
        binary.Write(&buffer, binary.BigEndian, icmp)
        // 請求的數據
        data := make([]byte, size)
        // 將請求數據寫到 icmp 報文頭后
        buffer.Write(data)
        data = buffer.Bytes()
        // ICMP 請求簽名(校驗和):相鄰兩位拼接到一起,拼接成兩個字節(jié)的數
        checkSum := checkSum(data)
        // 簽名賦值到 data 里
        data[2] = byte(checkSum >> 8)
        data[3] = byte(checkSum)
        startTime := time.Now()

        // 設置超時時間
        conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Millisecond))

        // 將 data 寫入連接中,
        n, err := conn.Write(data)
        if err != nil {
            log.Println(err)
            continue
        }
        // 發(fā)送數 ++
        SendCnt++
        // 接收響應
        buf := make([]byte, 1024)
        n, err = conn.Read(buf)
        //fmt.Println(data)
        if err != nil {
            log.Println(err)
            continue
        }
        // 接受數 ++
        RecCnt++
        //fmt.Println(n, err) // data:64,ip首部:20,icmp:8個 = 92 個
        // 打印信息
        t := time.Since(startTime).Milliseconds()
        fmt.Printf("來自 %d.%d.%d.%d 的回復:字節(jié)=%d 時間=%d TTL=%d\n", buf[12], buf[13], buf[14], buf[15], n-28, t, buf[8])
        MaxTime = Max(MaxTime, t)
        MinTime = Min(MinTime, t)
        SumTime += t
        time.Sleep(time.Second)
    }

    fmt.Printf("\n%s 的 Ping 統計信息:\n", remoteaddr)
    fmt.Printf("    數據包: 已發(fā)送 = %d,已接收 = %d,丟失 = %d (%.f%% 丟失),\n", SendCnt, RecCnt, count*2-SendCnt-RecCnt, float64(count*2-SendCnt-RecCnt)/float64(count*2)*100)
    fmt.Println("往返行程的估計時間(以毫秒為單位):")
    fmt.Printf("    最短 = %d,最長 = %d,平均 = %d\n", MinTime, MaxTime, SumTime/int64(count))
}

// 求校驗和
func checkSum(data []byte) uint16 {
    // 第一步:兩兩拼接并求和
    length := len(data)
    index := 0
    var sum uint32
    for length > 1 {
        // 拼接且求和
        sum += uint32(data[index])<<8 + uint32(data[index+1])
        length -= 2
        index += 2
    }
    // 奇數情況,還剩下一個,直接求和過去
    if length == 1 {
        sum += uint32(data[index])
    }

    // 第二部:高 16 位,低 16 位 相加,直至高 16 位為 0
    hi := sum >> 16
    for hi != 0 {
        sum = hi + uint32(uint16(sum))
        hi = sum >> 16
    }
    // 返回 sum 值 取反
    return uint16(^sum)
}

// GetCommandArgs 命令行參數
func GetCommandArgs() {
    flag.Int64Var(&timeout, "w", 1000, "請求超時時間")
    flag.IntVar(&size, "l", 32, "發(fā)送字節(jié)數")
    flag.IntVar(&count, "n", 4, "請求次數")
    flag.BoolVar(&helpFlag, "h", false, "顯示幫助信息")
    flag.Parse()
}

func Max(a, b int64) int64 {
    if a > b {
        return a
    }
    return b
}

func Min(a, b int64) int64 {
    if a < b {
        return a
    }
    return b
}

func displayHelp() {
    fmt.Println(`選項:
    -n count       要發(fā)送的回顯請求數。
    -l size        發(fā)送緩沖區(qū)大小。
    -w timeout     等待每次回復的超時時間(毫秒)。
    -h            幫助選項`)
}

作者:panco68120
鏈接:https://juejin.cn/post/7357142305423933494
來源:稀土掘金
著作權歸作者所有。商業(yè)轉載請聯系作者獲得授權,非商業(yè)轉載請注明出處。


請登錄后查看

小碼二開 最后編輯于2024-05-16 15:53:49

快捷回復
回復
回復
回復({{post_count}}) {{!is_user ? '我的回復' :'全部回復'}}
排序 默認正序 回復倒序 點贊倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理員 企業(yè)

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
{{item.is_suggest == 1? '取消推薦': '推薦'}}
沙發(fā) 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暫無簡介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打賞
已打賞¥{{item.reward_price}}
{{item.like_count}}
{{item.showReply ? '取消回復' : '回復'}}
刪除
回復
回復

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回復 {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打賞
已打賞¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回復' : '回復'}}
刪除
回復
回復
查看更多
打賞
已打賞¥{{reward_price}}
894
{{like_count}}
{{collect_count}}
添加回復 ({{post_count}})

相關推薦

快速安全登錄

使用微信掃碼登錄
{{item.label}} 加精
{{item.label}} {{item.label}} 板塊推薦 常見問題 產品動態(tài) 精選推薦 首頁頭條 首頁動態(tài) 首頁推薦
取 消 確 定
回復
回復
問題:
問題自動獲取的帖子內容,不準確時需要手動修改. [獲取答案]
答案:
提交
bug 需求 取 消 確 定
打賞金額
當前余額:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
請輸入 0.1-{{reward_max_price}} 范圍內的數值
打賞成功
¥{{price}}
完成 確認打賞

微信登錄/注冊

切換手機號登錄

{{ bind_phone ? '綁定手機' : '手機登錄'}}

{{codeText}}
切換微信登錄/注冊
暫不綁定
CRMEB客服

CRMEB咨詢熱線 咨詢熱線

400-8888-794

微信掃碼咨詢

CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
返回頂部 返回頂部
CRMEB客服