Go语言中实现UDP Ping并解析ICMP端口不可达错误

Go语言中实现UDP Ping并解析ICMP端口不可达错误

本文详细探讨了在go语言中实现udp ping以检测服务器口可达性的方法。核心内容包括理解icmp目标不可达消息的结构(特别是类型3、代码3),以及如何在接收到icmp数据包后,通过解析其前两个字节来识别“端口不可达”错误。文章同时指出标准udp套接字在直接捕获icmp错误方面的局限性,并引入了原始套接字(raw sockets)作为获取icmp数据的潜在途径。

引言:UDP Ping与ICMP错误检测

在客户端/服务器应用中,有时需要通过UDP协议来探测远程服务器上特定端口的可达性。与TCP的握手机制不同,UDP是无连接的,发送数据包后无法直接获得确认。然而,当UDP数据包发送到一个不可达的端口时,操作系统通常会响应一个ICMP(Internet Control Message Protocol)“目标不可达”消息,其中包含“端口不可达”的特定代码。通过捕获并解析这个ICMP消息,我们可以判断目标端口是否开放。

然而,标准的Go语言net.ListenUDP和conn.ReadFromUDP方法主要用于处理UDP数据包本身。当远程端口不可达时,内核会生成ICMP错误,但这些ICMP错误通常不会直接通过UDP套接字作为数据返回给应用程序。应用程序的ReadFromUDP调用可能只是超时或返回一个不包含具体ICMP信息的错误(例如,在某些情况下,错误可能为nil但读取到0字节)。因此,要精确地捕获并识别ICMP端口不可达错误,需要更底层的机制。

理解ICMP目标不可达消息

ICMP目标不可达消息(Destination Unreachable Message)是RFC 792中定义的一种ICMP类型,用于指示数据包无法送达目的地。其结构如下:

    0                   1                   2                   3    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |     Type      |     Code      |          Checksum             |   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |                             unused                            |   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |      Internet Header + 64 bits of Original Data Datagram      |   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

对于目标不可达消息:

立即学习“go语言免费学习笔记(深入)”;

Type (类型) 字段固定为 3。Code (代码) 字段指示了不可达的具体原因。其中,3 表示 端口不可达 (port unreachable)

其他常见的代码包括:

0 = 网络不可达 (net unreachable)1 = 主机不可达 (host unreachable)2 = 协议不可达 (protocol unreachable)4 = 需要分片但设置了DF位 (fragmentation needed and DF set)5 = 源路由失败 (source route failed)

因此,要识别“端口不可达”错误,我们需要检查接收到的ICMP消息的前两个字节:第一个字节为 3,第二个字节为 3。

Go语言中捕获与解析ICMP错误

标准UDP套接字的局限性

如前所述,使用net.ListenUDP创建的UDP套接字,其ReadFromUDP方法通常不会直接接收到内核生成的ICMP错误消息。这些错误通常由操作系统内核在内部处理,并可能以套接字操作错误(如ECONNREFUSED,尽管这在UDP中不常见且通常与TCP相关)或超时的方式间接通知应用程序。在Go语言中,如果ReadFromUDP返回nil错误和0字节,这通常意味着在设定的读取时间内没有收到任何UDP数据包,但并不直接表明收到了ICMP错误。

使用原始套接字(Raw Sockets)捕获ICMP

为了直接捕获ICMP数据包,应用程序通常需要使用原始套接字(Raw Sockets)。原始套接字允许应用程序绕过传输层(如UDP或TCP),直接在网络层(IP层)上发送和接收数据包,包括ICMP数据包。

在Go语言中,可以通过syscall包或更高级的golang.org/x/net/icmp包来操作原始套接字。然而,使用原始套接字通常需要:

管理员权限: 在大多数操作系统上,创建和使用原始套接字需要root或管理员权限,以防止恶意应用程序滥用网络资源。平台差异: 原始套接字的行为和权限要求可能因操作系统而异(Linux、Windows、macOS等)。

捕获ICMP错误的基本流程是:

创建一个原始套接字,监听IP协议号为ICMP的数据包。发送UDP ping数据包到目标地址和端口。在原始套接字上监听传入的ICMP数据包。接收到ICMP数据包后,解析其内容以识别端口不可达错误。

解析ICMP目标不可达消息示例

假设我们已经通过原始套接字成功捕获了一个ICMP数据包,并将其存储在一个字节切片中(例如icmpReply)。我们可以通过检查其前两个字节来判断是否为“端口不可达”错误。

package mainimport (    "fmt"    "net"    "time"    "errors"    "golang.org/x/net/icmp"    "golang.org/x/net/ipv4"    "syscall")// simulateSendUDPPing 模拟发送UDP数据包,实际中会发送到目标func simulateSendUDPPing(conn *net.UDPConn, dst *net.UDPAddr) error {    _, err := conn.WriteTo([]byte("PING"), dst)    if err != nil {        return fmt.Errorf("failed to send UDP ping request: %w", err)    }    fmt.Printf("Sent UDP PING to %sn", dst.String())    return nil}// listenForICMPError 尝试监听ICMP错误,这通常需要原始套接字// 注意:此函数是一个简化示例,实际生产环境需要更健壮的错误处理和权限管理func listenForICMPError(network string, listenAddr *net.IPAddr, timeout time.Duration) ([]byte, error) {    // 使用 ip4:icmp 协议创建原始套接字    // 在非Linux系统上,可能需要不同的方法或更高的权限    // Windows下,通常不能直接监听ICMP,需要使用更高级的API    // macOS下,也需要root权限    conn, err := icmp.ListenPacket(network, listenAddr.String())    if err != nil {        return nil, fmt.Errorf("failed to listen for ICMP packets: %w. On Linux, this often requires root privileges.", err)    }    defer conn.Close()    fmt.Printf("Listening for ICMP packets on %s...n", listenAddr.String())    conn.SetReadDeadline(time.Now().Add(timeout))    rb := make([]byte, 1500) // 足够大的缓冲区来接收ICMP消息    n, peer, err := conn.ReadFrom(rb)    if err != nil {        if netErr, ok := err.(net.Error); ok && netErr.Timeout() {            return nil, errors.New("ICMP read timeout, no ICMP error received")        }        return nil, fmt.Errorf("failed to read from ICMP socket: %w", err)    }    fmt.Printf("Received %d bytes from %sn", n, peer)    return rb[:n], nil}// parseICMPDestinationUnreachable 解析ICMP目标不可达消息func parseICMPDestinationUnreachable(icmpReply []byte) (bool, error) {    if len(icmpReply) < ipv4.HeaderLen + icmp.HeaderLen { // 至少包含IP头和ICMP头        return false, errors.New("ICMP reply too short to be a valid ICMP message")    }    // 假设icmpReply已经移除了IP头,只包含ICMP数据    // 如果是原始套接字直接接收,可能包含IP头,需要先解析IP头    // 这里我们使用golang.org/x/net/icmp包来解析,它会处理IP头    msg, err := icmp.ParseMessage(syscall.IPPROTO_ICMP, icmpReply)    if err != nil {        return false, fmt.Errorf("failed to parse ICMP message: %w", err)    }    if msg.Type == ipv4.ICMPTypeDestinationUnreachable {        if msg.Code == 3 { // Code 3 for Port Unreachable            fmt.Println("Detected ICMP Port Unreachable error!")            return true, nil        } else {            fmt.Printf("Detected ICMP Destination Unreachable with code %d (not port unreachable)n", msg.Code)            return false, nil        }    } else {        fmt.Printf("Received ICMP message of type %s (not Destination Unreachable)n", msg.Type)    }    return false, nil}func main() {    // 1. 设置源和目标地址    srcAddr := &net.UDPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0} // 任意本地端口    dstAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9999} // 目标地址和不可达端口    // 2. 创建UDP连接用于发送PING    udpConn, err := net.ListenUDP("udp4", srcAddr)    if err != nil {        fmt.Printf("Failed to listen UDP: %vn", err)        return    }    defer udpConn.Close()    // 3. 准备ICMP监听器 (需要root权限)    // 在Windows上,ListenPacket("ip4:icmp", ...)可能不工作,需要更底层的API或管理员权限    // 在macOS/Linux上,需要sudo运行    icmpListenAddr, err := net.ResolveIPAddr("ip4", "0.0.0.0")    if err != nil {        fmt.Printf("Failed to resolve ICMP listen address: %vn", err)        return    }    // 4. 发送UDP PING    if err := simulateSendUDPPing(udpConn, dstAddr); err != nil {        fmt.Printf("Error sending UDP ping: %vn", err)        return    }    // 5. 监听ICMP错误    // 注意:这里需要并行运行,因为UDP发送和ICMP接收是异步的    // 实际应用中,你可能需要在一个goroutine中发送,另一个goroutine中监听ICMP    // 为了示例清晰,这里简化处理,假设ICMP会在短时间内返回    icmpResponse, err := listenForICMPError("ip4:icmp", icmpListenAddr, 500*time.Millisecond)    if err != nil {        fmt.Printf("Error listening for ICMP: %vn", err)        // 如果是超时,可能是端口可达,或者防火墙阻止了ICMP        return    }    // 6. 解析ICMP响应    isPortUnreachable, err := parseICMPDestinationUnreachable(icmpResponse)    if err != nil {        fmt.Printf("Error parsing ICMP response: %vn", err)        return    }    if isPortUnreachable {        fmt.Println("Conclusion: Target UDP port is likely unreachable.")    } else {        fmt.Println("Conclusion: No ICMP Port Unreachable error detected (might be reachable, or other ICMP error).")    }}

代码说明:

simulateSendUDPPing 模拟了向目标发送UDP数据包的过程。listenForICMPError 函数尝试使用golang.org/x/net/icmp.ListenPacket来创建一个原始ICMP套接字。它监听ip4:icmp协议,这意味着它会接收所有IPv4 ICMP数据包。parseICMPDestinationUnreachable 函数接收一个字节切片,并尝试使用icmp.ParseMessage解析它。然后,它检查消息的Type是否为ipv4.ICMPTypeDestinationUnreachable(即Type 3),以及Code是否为3(端口不可达)。main 函数展示了如何组合这些步骤。它首先发送UDP数据包,然后尝试监听并解析可能返回的ICMP错误。

重要提示:上述listenForICMPError函数中的icmp.ListenPacket在大多数Linux系统上需要root权限才能成功创建原始套接字。在Windows或macOS上,原始套接字的操作可能有所不同,或者也需要管理员权限。这是实现这种低级别网络探测的关键挑战。

注意事项

权限问题: 原始套接字操作通常需要管理员或root权限。在生产环境中部署时,需要仔细考虑安全性和权限管理。防火墙 中间件防火墙可能会过滤ICMP消息,导致即使端口不可达也收不到ICMP回复。操作系统差异: 原始套接字和ICMP处理在不同操作系统上可能存在细微差异。golang.org/x/net/icmp包旨在提供跨平台兼容性,但底层系统调用仍可能不同。超时处理: 如果目标端口可达,或者ICMP消息被过滤,将不会收到ICMP错误。因此,设置合理的读取超时时间至关重要。超时可以作为端口可达的一种间接指示。并发性: 发送UDP数据包和监听ICMP响应是两个异步操作。在实际应用中,通常会使用Go协程(goroutines)来并行处理这两个任务。

总结

通过UDP ping探测远程端口的可达性,并解析ICMP“端口不可达”错误,是诊断网络问题和验证服务状态的有效方法。虽然标准的Go UDP套接字无法直接捕获这些ICMP错误,但通过使用原始套接字(如golang.org/x/net/icmp包),我们可以监听并解析ICMP消息。理解ICMP目标不可达消息的结构,特别是类型3、代码3的含义,是正确识别“端口不可达”错误的关键。在实际实现时,务必考虑权限、防火墙以及操作系统兼容性等因素。

以上就是Go语言中实现UDP Ping并解析ICMP端口不可达错误的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1419987.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Golang如何实现Web API版本控制_Golang Web API版本控制实践详解
上一篇 2025年12月16日 13:39:00
Linux开发Golang环境快速部署示例
下一篇 2025年12月16日 13:39:11

相关推荐

  • linux查看内存使用情况

    linux查看内存使用情况     在做Linux系统优化的时候,物理内存是其中最重要的一方面。自然的,Linux也提供了非常多的方法来监控宝贵的内存资源的使用情况。下面的清单详细的列出了Linux系统下通过视图工具或命令行来查看内存使用情况的各种方法。 推荐:【Linux视频教程】 1. /pro…

    2026年8月28日
    100
  • linux安装gcc命令

    linux安装gcc命令 了解一下gcc gcc是GNU编译器套件(GNU Compiler Collection),它包括了C、C++、Objective-C、Fortran、Java、Ada、Go语言和D语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)。GCC的初衷是为…

    2026年8月28日
    000
  • Win10照片合成视频功能将关闭自动同步

    微软在windows 10系统中内置的照片与视频应用程序均具备编辑功能,可用于调整图片或剪辑视频等内容。其中,照片应用在编辑图片时还支持自动保存至云端,这一功能默认状态下会将修改后的文件上传至onedrive。然而,由于该功能的使用频率较低或微软认为无需进一步投入资源维护,该公司计划于明年年初终止此…

    2026年8月28日
    000
  • 抖音直播怎么放音乐?直播回放有访客记录吗

    随着抖音直播的不断发展,越来越多的内容创作者和品牌选择通过这一形式进行推广。在直播过程中,音乐作为氛围调节的重要工具,不仅能提升观看体验,还能增强互动性。那么,如何在抖音直播中合理使用音乐呢?本文将从多个角度为您解答。 一、抖音直播音乐使用的注意事项 1. 匹配直播主题 选择与直播内容风格相符的音乐…

    2026年8月28日
    000
  • 谷歌制裁影响分析_涉及人员数量与背景解读

    谷歌制裁的影响远超数字,它深刻重塑了技术生态与人才流动。受制裁企业因无法使用gms及核心技术受限,被迫加速自主替代,引发人才双向流动:一方面部分国际化人才流失,另一方面国内基础技术领域需求激增,推动人才向操作系统、芯片等国产化方向回流。全球技术生态因此呈现碎片化趋势,区域性技术联盟兴起,创新效率下降…

    2026年8月28日
    100
  • Win10系统没有投影功能该如何解决?

    Win10系统没有投影功能该如何解决?Win10系统没有投影功能该如何解决?Win10系统没有投影功能该如何解决?Win10系统没有投影功能该如何解决?

    在win10操作系统里,投影到此电脑的功能可以让其他设备借助无线网络将画面投射到本机屏幕上,然而部分用户反映自己的设备并未显示该选项。若遇到此类情况,可尝试按照以下步骤重新添加此功能。有需求的朋友不妨参考一下。 具体操作如下: 1、在开始菜单上点击鼠标右键,然后从出现的列表中选取“Windows P…

    2026年8月28日 用户投稿
    100
  • linux重启服务命令

    linux重启服务命令     重启: service 服务名 restart 或 systemctl restart 服务名 service和systemctl 1.service命令 service命令其实是去/etc/init.d目录下,去执行相关程序 # service命令启动redis脚本…

    2026年8月28日
    100
  • 利用window自带的powershell进行文件哈希值校验

    通常为了保证我们从网上下载的文件的完整性和可靠性,我们把文件下载下来以后都会校验一下md5值或sha1值(例如验证[下载的win10 iso镜像]是否为原始文件),这一般都需要借助专门的md5检验工具来完成。但其实使用windows系统自带的windows powershell运行命令即可进行文件m…

    2026年8月28日
    000
  • VSCode怎么设置代码对齐_VSCode代码对齐与缩进规则配置教程

    答案:VSCode通过配置格式化工具和扩展实现代码对齐,需安装对应语言的工具(如Prettier、autopep8),启用formatOnSave自动格式化,或使用Shift+Alt+F手动格式化;通过.editorconfig统一团队风格,检查文件类型识别和扩展冲突解决格式化失效问题;在setti…

    2026年8月28日
    000
  • linux touch命令详解

    linux touch命令详解linux touch命令详解linux touch命令详解linux touch命令详解

    linux touch命令详解: 1、命令功能: 创建文件和修改文件或者目录的时间戳 2、命令语法:  touch  【选项】 【文件名或者目录名】 3、命令参数: -a  只修改文件的access(访问)时间. -c  或–no-create  不创建不存在的文件。 -d  使用指定的…

    2026年8月28日 用户投稿
    000
  • linux vi命令详解

    linux vi命令详解 推荐:【Linux视频教程】 vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里介绍一下它的用法和一部分指令。 由于 对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方进一步了解它…

    2026年8月28日
    000
  • win11怎么更改图片格式后缀

    win11怎么更改图片格式后缀win11怎么更改图片格式后缀win11怎么更改图片格式后缀win11怎么更改图片格式后缀

    有时我们在使用电脑时,可能需要对图片文件的格式做一些调整。本文将介绍如何在windows 11中更改图片的后缀名。 提示:如果您正在寻找一种简单的方式来升级到Windows 11,可以尝试使用小白一键重装系统工具,它现在已支持Windows 11的一键升级功能。 第一步,在您的Windows 11桌…

    2026年8月28日 用户投稿
    100
  • 谷歌浏览器下载完成但无法打开文件怎么办

    先检查文件是否被系统锁定,右键文件属性中勾选“解除锁定”并确认;再核对文件类型与关联程序,确保安装了对应软件;最后清理浏览器缓存或重置设置,基本可解决下载文件打不开的问题。 下载完成却打不开文件,这问题挺常见,别急着重装浏览器,先试试这几个办法,基本都能搞定。 检查文件是否被系统锁定 Windows…

    2026年8月28日
    000
  • 查看df的帮助说明命令

    查看df的帮助说明命令     查看df的帮助说明的命令可以使用man df,也可以使用df –help以及whatis df。 whatis:用于查询一个命令执行什么功能,并将查询结果打印到终端上。 man:会按照系统设计帮助目录,在各个文档里面查询,并且显示内容。 df &#8211…

    2026年8月28日
    000
  • 亚马逊浏览器指纹是什么意思 亚马逊账号防关联技术原理

    亚马逊通过浏览器指纹追踪用户,防关联需使用独立IP、不同浏览器与操作系统等技术,结合VPS、代理IP、虚拟机等工具模拟真实用户环境,避免账号关联风险。 亚马逊浏览器指纹是用于识别和追踪用户在亚马逊平台上的活动的一种技术手段,它通过收集用户浏览器和设备的各种属性信息,生成一个唯一的“指纹”,用于区分不…

    2026年8月28日
    100
  • 基于Windows的渗透测试虚拟机系统

    基于Windows的渗透测试虚拟机系统基于Windows的渗透测试虚拟机系统基于Windows的渗透测试虚拟机系统基于Windows的渗透测试虚拟机系统

    今天我们将为大家详细介绍一款名为commando vm的渗透测试虚拟机。这是一款基于windows的高度可定制的渗透测试虚拟机环境,目前已发布正式版本,适用于渗透测试和红队研究。 工具安装 基础要求:建议在安装Commando VM之前,确保虚拟机已更新至最新版本,并检查更新、重启设备并确认更新已完…

    2026年8月28日 用户投稿
    100
  • 《异环》「收容测试」PC端测试现已开启丨鉴定师,该你登场啦!

    《异环》「收容测试」PC端测试现已开启丨鉴定师,该你登场啦!《异环》「收容测试」PC端测试现已开启丨鉴定师,该你登场啦!《异环》「收容测试」PC端测试现已开启丨鉴定师,该你登场啦!《异环》「收容测试」PC端测试现已开启丨鉴定师,该你登场啦!

    「收容测试」PC端现已开放体验! 开放都市题材新作《异环》正式开启「收容测试」PC端体验活动! 「收容测试」PC端下载链接>>https://www.php.cn/link/0a7ff96e4fa7c92a13ccab013d580930 你未曾参与的这段时间,海特洛发生了许多变化,快来…

    2026年8月28日 用户投稿
    100
  • Win10录制视频快捷键在哪更改?

    我们都知道,windows 10 系统内置了视频录制功能。通常情况下,只需按下 win+g 组合键就能调出 xbox 游戏录制工具栏,而按下 win+alt+r 则能停止录制。然而,如果这些默认快捷键与某些游戏中的快捷键发生冲突,我们可以通过调整其中一方的快捷键来解决这个问题。接下来,我们将详细介绍…

    2026年8月28日
    300
  • 编译JDK 12时遇到“BitMap index out of bounds”错误该如何解决?

    编译JDK 12时遭遇“BitMap index out of bounds”错误导致编译失败,这通常表示程序试图访问位图中不存在的索引。此类错误常发生于底层代码,例如垃圾回收或内存管理模块。错误信息指出/home/lx/my_jdk/jdk-jdk-12-ga/src/hotspot/share/…

    2026年8月28日
    100
  • 查看网卡中断命令

    查看网卡中断命令 Linux中查看网卡中断的命令是:cat proc/interrupts cat是linux中读取文件并输出到屏幕的命令,cat proc/interrupts读取到的内容从左到右,分别为:1、逻辑中断号,2、中断在各CPU发生的次数,3、中断所属设备类名称,4、硬件中断号,5、中…

    2026年8月28日
    100

发表回复

登录后才能评论
关注微信