Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Go语言中利用ICMP检测UDP端口可达性教程_创想鸟

Go语言中利用ICMP检测UDP端口可达性教程

Go语言中利用ICMP检测UDP端口可达性教程

本教程详细阐述了在go语言中如何通过发送udp探测包并监听icmp“口不可达”消息来检测远程udp端口的可达性。文章解释了udp协议的无连接特性,以及icmp type 3 code 3消息的原理,并提供了使用`golang.org/x/net/icmp`库实现这一机制的专业指南和示例代码,同时强调了相关的注意事项。

UDP端口可达性检测的原理

UDP(用户数据报协议)是一种无连接协议,它不提供像TCP那样的握手机制来确认连接的建立或端口的监听状态。因此,传统的“ping”工具(基于ICMP Echo Request/Reply)无法直接用于检测特定UDP端口的开放状态。然而,当一个UDP数据包被发送到一个目标主机的特定端口,而该端口上没有应用程序在监听时,操作系统的网络栈通常会生成一个ICMP(互联网控制消息协议)“目标不可达”消息,并将其发送回源主机。

具体来说,这种情况下产生的ICMP消息类型为3(Destination Unreachable),代码为3(Port Unreachable)。这个机制可以被利用来间接判断一个远程UDP端口是否处于非监听状态。

根据RFC792的定义,ICMP“目标不可达”消息的结构如下:

Destination Unreachable Message    0                   1                   2                   3    0 1 2 3 4 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      |   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   IP Fields:   Destination Address      The source network and address from the original datagram's data.   ICMP Fields:   Type      3   Code      0 = net unreachable;      1 = host unreachable;      2 = protocol unreachable;      3 = port unreachable;      4 = fragmentation needed and DF set;      5 = source route failed.

其中,Type字段为3表示“目标不可达”,Code字段为3表示“端口不可达”。通过解析接收到的ICMP消息的这两个字段,我们可以判断UDP探测包是否遇到了一个未监听的端口。

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

Go语言中实现UDP端口可达性检测的挑战

在Go语言中,标准库net提供的net.UDPConn.ReadFromUDP方法主要用于读取UDP套接字接收到的UDP数据包。当发送的UDP探测包触发了ICMP“端口不可达”错误时,这个ICMP错误消息通常不会直接通过ReadFromUDP返回给应用程序的UDP套接字。这是因为ICMP错误消息是在IP层由操作系统内核处理和生成的,而不是作为UDP数据包传递给应用程序。因此,尝试通过ReadFromUDP来捕获ICMP错误通常会失败,表现为ReadFromUDP返回0字节和nil错误(如果设置了超时,则可能返回超时错误),因为它没有收到任何UDP数据。

为了捕获ICMP错误消息,我们需要使用更底层的网络接口,即原始套接字(Raw Socket),它允许应用程序直接发送和接收IP层的数据包,包括ICMP消息。

通过ICMP原始套接字实现检测

在Go语言中,我们可以借助golang.org/x/net/icmp库来创建和管理ICMP原始套接字,从而实现UDP端口可达性的检测。其核心思路是:

创建一个UDP连接用于发送探测数据包。创建一个ICMP原始套接字用于监听可能返回的ICMP错误消息。向目标地址的非监听端口发送UDP探测包。从ICMP套接字读取并解析接收到的ICMP消息,检查其类型和代码是否为“目标不可达”和“端口不可达”。

示例代码

以下是一个Go语言示例,演示了如何实现UDP端口可达性检测:

package mainimport (    "errors"    "fmt"    "log"    "net"    "os"    "time"    "golang.org/x/net/icmp"    "golang.org/x/net/ipv4")// UDPPortCheckResult 定义端口检测结果type UDPPortCheckResult struct {    Reachable bool    Error     error}// CheckUDPPortReachability 发送UDP探测包并监听ICMP回复以检测端口可达性func CheckUDPPortReachability(targetAddr string, timeout time.Duration) UDPPortCheckResult {    // 1. 解析目标地址    addr, err := net.ResolveUDPAddr("udp4", targetAddr)    if err != nil {        return UDPPortCheckResult{false, fmt.Errorf("解析目标地址失败: %w", err)}    }    // 2. 创建UDP连接用于发送探测包    // 选择一个随机的本地端口    udpConn, err := net.ListenUDP("udp4", nil)    if err != nil {        return UDPPortCheckResult{false, fmt.Errorf("创建UDP发送连接失败: %w", err)}    }    defer udpConn.Close()    // 3. 创建ICMP原始套接字用于监听回复    // "ip4:icmp" 表示监听IPv4的ICMP协议    icmpConn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0")    if err != nil {        return UDPPortCheckResult{false, fmt.Errorf("创建ICMP监听连接失败: %w", err)}    }    defer icmpConn.Close()    // 设置ICMP连接的读取超时    if err := icmpConn.SetReadDeadline(time.Now().Add(timeout)); err != nil {        return UDPPortCheckResult{false, fmt.Errorf("设置ICMP读取超时失败: %w", err)}    }    // 4. 发送UDP探测包    message := []byte("UDP Port Probe")    if _, err := udpConn.WriteTo(message, addr); err != nil {        return UDPPortCheckResult{false, fmt.Errorf("发送UDP探测包失败: %w", err)}    }    // 5. 从ICMP套接字读取并解析回复    buffer := make([]byte, 1500) // 通常ICMP报文不会太大    for {        n, peer, err := icmpConn.ReadFrom(buffer)        if err != nil {            // 如果是超时错误,则认为端口可达(没有收到不可达回复)            if netErr, ok := err.(net.Error); ok && netErr.Timeout() {                return UDPPortCheckResult{true, nil} // 超时,认为端口可达            }            return UDPPortCheckResult{false, fmt.Errorf("读取ICMP回复失败: %w", err)}        }        // 确保回复来自目标主机        if peer.String() != addr.IP.String() {            continue // 忽略来自其他主机的ICMP回复        }        // 解析ICMP消息        // 注意:icmp.ParseMessage期望的是ICMP报文,而不是整个IP报文。        // 在Linux/Unix上,ListenPacket("ip4:icmp")通常直接返回ICMP报文。        // 在Windows上可能需要手动剥离IP头。这里假设是直接ICMP报文。        // 更严谨的做法是使用 ipv4.ParseHeader 来检查IP头,然后提取ICMP部分。        // 但 icmp.ListenPacket 通常会处理好这些。        msg, err := icmp.ParseMessage(ipv4.ICMPType, buffer[:n])        if err != nil {            log.Printf("解析ICMP消息失败: %v", err)            continue // 尝试读取下一个        }        switch msg.Type {        case ipv4.ICMPTypeDestinationUnreachable:            if msg.Code == icmp.DstUnreachPort {                // 收到端口不可达错误,说明端口未开放                return UDPPortCheckResult{false, errors.New("端口不可达 (ICMP Type 3, Code 3)")}            }            // 其他目标不可达错误,可能表示网络或主机问题            return UDPPortCheckResult{false, fmt.Errorf("目标不可达 (ICMP Type %d, Code %d)", msg.Type, msg.Code)}        case ipv4.ICMPTypeEchoReply:            // 收到ICMP Echo Reply,这不是我们期望的,但表示主机存活            // 这种情况下,UDP端口可能开放,也可能只是主机响应了ping            // 继续等待或视为可达            // log.Printf("收到ICMP Echo Reply,可能端口可达")            // return UDPPortCheckResult{true, nil} // 暂时认为可达        default:            // 收到其他ICMP消息,继续等待或忽略            // log.Printf("收到其他ICMP消息: Type %d, Code %d", msg.Type, msg.Code)        }    }}func main() {    if len(os.Args) < 3 {        fmt.Println("用法: go run main.go  ")        fmt.Println("例如: go run main.go 127.0.0.1 8080")        return    }    targetIP := os.Args[1]    targetPort := os.Args[2]    targetAddr := net.JoinHostPort(targetIP, targetPort)    timeout := 2 * time.Second    fmt.Printf("检测UDP端口 %s 的可达性...n", targetAddr)    result := CheckUDPPortReachability(targetAddr, timeout)    if result.Reachable {        fmt.Printf("UDP端口 %s 似乎是可达的 (未收到ICMP端口不可达错误).n", targetAddr)    } else {        fmt.Printf("UDP端口 %s 不可达: %vn", targetAddr, result.Error)    }}

代码解析

CheckUDPPortReachability(targetAddr string, timeout time.Duration) 函数: 这是核心函数,负责执行检测逻辑。net.ListenUDP(“udp4”, nil): 创建一个UDP连接,用于发送探测包。nil参数表示让操作系统自动选择一个可用的本地IP地址和端口。icmp.ListenPacket(“ip4:icmp”, “0.0.0.0”): 这是关键步骤,它创建一个ICMP原始套接字。”ip4:icmp” 参数告诉系统我们想监听IPv4的ICMP协议数据包。”0.0.0.0″ 表示监听所有本地接口上的ICMP数据包。权限注意: 创建原始套接字通常需要root权限(在Linux上是CAP_NET_RAW能力)。如果程序没有足够的权限,icmp.ListenPacket会失败。udpConn.WriteTo(message, addr): 向目标地址发送一个简单的UDP数据包。这个数据包的目的就是为了触发ICMP错误。icmpConn.SetReadDeadline(time.Now().Add(timeout)): 为ICMP读取操作设置一个超时。如果在超时时间内没有收到ICMP回复,我们通常可以假定端口是可达的(即没有收到“端口不可达”错误)。icmpConn.ReadFrom(buffer): 从ICMP原始套接字读取数据。这里接收到的数据是原始的ICMP消息。icmp.ParseMessage(ipv4.ICMPType, buffer[:n]): 解析接收到的字节流,将其转换为icmp.Message结构。ipv4.ICMPType指定了我们期望的ICMP协议类型。switch msg.Type: 根据ICMP消息的类型进行判断。当msg.Type为ipv4.ICMPTypeDestinationUnreachable且msg.Code为icmp.DstUnreachPort时,我们确认收到了“端口不可达”错误,表明目标UDP端口未开放。如果超时,或者收到其他类型的ICMP消息(例如ICMPTypeEchoReply),则认为端口是可达的,因为没有明确的“端口不可达”指示。

注意事项

权限要求: 使用golang.org/x/net/icmp创建原始套接字通常需要root权限或在Linux上具有CAP_NET_RAW能力。在非root用户下运行可能会导致permission denied错误。在Linux上,可以通过sudo setcap cap_net_raw+ep /path/to/your/executable来赋予特定可执行文件此能力,使其无需root即可运行。防火墙: 目标主机或中间网络设备上的防火墙可能会过滤ICMP消息,导致即使端口不可达也收不到ICMP回复。这可能导致误判为端口可达。网络设备行为: 并非所有路由器或防火墙都会为UDP端口不可达生成ICMP消息。某些设备可能会静默丢弃数据包,这也会导致误判。超时处理: 合理设置超时时间至关重要。如果超时过短,可能在ICMP回复到达前就判断为可达;如果过长,会影响检测效率。并发与资源: 如果需要对大量端口进行检测,需要注意并发控制和系统资源(如文件描述符)的使用。IP版本: 示例代码使用的是IPv4 (udp4, ip4:icmp)。如果需要支持IPv6,则需要相应地使用udp6和ip6:icmp。错误处理: 在实际应用中,需要更完善的错误处理机制,例如区分网络错误和逻辑错误。

总结

通过利用ICMP“目标不可达”消息(Type 3, Code 3),我们可以在Go语言中实现对远程UDP端口可达性的检测。虽然标准UDP套接字无法直接接收这些ICMP错误,但golang.org/x/net/icmp库提供了一种有效的方法来创建原始ICMP套接字并监听这些消息。然而,在实现过程中必须注意权限、防火墙、网络设备行为以及超时设置等关键因素,以确保检测的准确性和可靠性。这种技术对于服务发现、健康检查或网络诊断等场景具有重要意义。

以上就是Go语言中利用ICMP检测UDP端口可达性教程的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Golang如何实现云原生应用日志聚合与分析_Golang云原生应用日志聚合分析实践详解
上一篇 2025年12月16日 13:51:23
解决Go语言中无法导入包的问题
下一篇 2025年12月16日 13:51:30

相关推荐

  • windows怎么关闭操作中心通知_禁用系统和应用通知消息教程

    关闭Windows通知可提升专注度,首先通过“设置-系统-通知”关闭所有通知或按需保留;其次启用“专注助手”在特定场景屏蔽干扰;再通过任务栏设置隐藏操作中心图标;专业版用户可通过组策略禁用操作中心;家庭版可修改注册表HKEY_CURRENT_USERSoftwarePoliciesMicrosoft…

    2026年9月22日
    100
  • 如何在mysql中搭建Percona XtraDB Cluster

    部署PXC需先配置系统环境并安装Percona源,随后在首个节点通过bootstrap启动集群,配置wsrep参数并创建SST用户,其他节点按相同配置加入集群,通过SHOW STATUS验证集群状态,确保cluster_size、wsrep_ready和cluster_status正常。 在MySQ…

    2026年9月22日
    100
  • Linux平台下的Eclipse配置

    在linux平台上配置eclipse时,可能会遇到一些常见的问题和优化需求。本文将详细介绍如何解决这些问题,并提供优化eclipse的建议。 启动Eclipse报错 启动Eclipse时,如果遇到以下错误: A Java Runtime Environment (JRE) or Java Devel…

    2026年9月22日
    000
  • 怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法

    怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法怎样在iPhone情侣模式中分享视频?快速上传和同步的实用方法

    最实用的方法是使用iCloud共享相簿,它支持情侣间视频快速上传与实时同步。首先双方需开启iCloud照片中的共享相簿功能,然后创建专属相簿并邀请对方加入。此后,任一方添加的视频会自动同步至对方设备,且不占用个人iCloud空间。相比AirDrop(限近距离)、即时通讯工具(压缩画质)或云盘(占存储…

    2026年9月22日 用户投稿
    000
  • 如何判断网络延迟(Ping值)过高是由运营商问题还是自身设备引起的?

    先重启设备并检查本地连接,若有线网络延迟降低则问题在无线信号;通过ping网关和外部DNS判断故障范围,结合tracert追踪路径,前几跳异常指向运营商问题,最后用官方测速工具验证并联系客服解决。 判断网络延迟高是运营商还是自身设备的问题,关键在于逐步隔离排查。从最接近你的设备开始测试,向外层层推进…

    2026年9月22日
    100
  • 悟空浏览器下载速度慢怎么解决_悟空浏览器下载速度慢提升技巧

    下载慢可优化缓存、网络、后台、多线程及版本。1、清理缓存与数据;2、检查Wi-Fi并优化信号;3、关闭后台应用释放资源;4、开启多线程加速下载;5、更新浏览器至最新版本。 如果您在使用悟空浏览器下载文件时遇到速度缓慢的情况,这可能是由于网络连接不稳定、浏览器缓存堆积或后台资源被大量占用所致。优化相关…

    2026年9月22日
    600
  • 常见分布式任务调度工具浅析

    一、背景 在日常业务中,经常会遇到需要在特定时间执行任务或周期性执行任务的需求。这些任务通常被归类为定时任务。为了满足这种需求,各种任务调度框架应运而生,如Timer、ScheduledThreadPoolExecutor(没错,它也可以用于定时任务)、Quartz等。然而,随着分布式和微服务的发展…

    2026年9月22日
    100
  • win11平板模式无法自动切换怎么办_win11平板模式自动切换异常修复方法

    首先检查设备形态和设置,确保键盘分离或屏幕翻转到位,并在“平板电脑”设置中启用自动切换;若无效,重启Windows资源管理器、更新传感器驱动或重装感应式旋转锁驱动;最后通过sfc /scannow命令修复系统文件,恢复平板模式自动切换功能。 如果您发现Windows 11的平板模式无法根据设备形态自…

    2026年9月22日
    100
  • Canva中AI生成图片如何导出?教你快速保存设计作品的方法

    答案:Canva中导出AI生成图片的操作与普通图片相同,点击右上角“分享”按钮,选择“下载”,可选PNG、JPG、PDF、SVG、MP4或GIF等格式;为保证画质,建议优先选用PNG格式,避免有损压缩,同时选择高分辨率和合适尺寸,Pro用户可进一步调整质量与透明背景设置;除下载外,还可通过分享链接、…

    2026年9月22日
    800
  • windows怎么用命令行删除顽固文件_命令行删除顽固文件的方法

    使用命令行可强制删除顽固文件,先以管理员身份运行cmd或PowerShell,通过del /f /q或Remove-Item -Force命令删除文件,若被占用则用taskkill终止进程,或进入安全模式删除,也可用rd /s /q删除整个目录。 如果您尝试删除某个文件时,发现资源管理器无法完成操作…

    2026年9月22日
    200
  • VSCode安全更新机制解析

    VSCode通过自动检查、数字签名验证和用户可控策略确保更新安全。启动时后台定期HTTPS请求检查新版本,每日一次;安装包经平台特定签名(Windows Authenticode、macOS代码签名、Linux GPG)验证完整性;用户可选自动更新、提示或关闭,企业可集中管控;微软通过安全入口响应漏…

    2026年9月22日
    100
  • Linux系统中文件属性和权限实战操作

    Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作Linux系统中文件属性和权限实战操作

    —–原本今天的文章是昨天晚上就要更新的,但是由于昨天晚上下班回到住的地方,发现停电了,所以就没写成。今天是在上一篇文章–linux系统中文件类型的基础上,继续进行深入的学习。好了,直接开干。 一、文件的操作权限: 1、在这之前我想还是很有必要介绍对文件的操作权限(…

    2026年9月22日 用户投稿
    200
  • Inkscape如何导出AI生成的矢量图片?教你快速保存图像的步骤

    答案:在Inkscape中导出矢量图需根据用途选择格式,网页用优化SVG并转文本为路径,印刷则导出为PDF/EPS、转文字为路径、确保高分辨率位图,同时注意颜色模式与出血设置。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 在Inkscap…

    2026年9月22日
    700
  • Laravel 8 登录后重定向至仪表盘的策略与实践

    本教程详细阐述了在 Laravel 8 中实现用户登录后重定向到仪表盘的多种策略。我们将探讨如何通过配置 LoginController 的 $redirectTo 属性、利用 RouteServiceProvider 定义常量以及在自定义登录方法中进行精确控制来管理重定向流程。文章还涵盖了相关中间…

    2026年9月22日
    000
  • 如何在iPhone情侣模式中启用视频通话?快速连接彼此的设置方法

    如何在iPhone情侣模式中启用视频通话?快速连接彼此的设置方法如何在iPhone情侣模式中启用视频通话?快速连接彼此的设置方法如何在iPhone情侣模式中启用视频通话?快速连接彼此的设置方法如何在iPhone情侣模式中启用视频通话?快速连接彼此的设置方法

    iPhone虽无官方“情侣模式”,但可通过FaceTime或微信、WhatsApp等第三方应用实现高质量视频通话。首选FaceTime,操作便捷、画质清晰,支持SharePlay共享影音,仅限苹果设备;跨平台可选微信、WhatsApp等,注重隐私可用Telegram。优化体验需稳定网络、良好光线与背…

    2026年9月22日 用户投稿
    200
  • windows8怎么更改锁屏界面的图片_windows8修改锁屏壁纸的方法

    1、可通过“电脑设置”或“控制面板”更换Windows 8.1锁屏图片;2、在“个性化”中选择“锁屏”并浏览自定义图像完成设置。 如果您希望个性化您的Windows 8设备,更改锁屏界面的图片是一个简单而有效的方法。系统提供了多种途径来设置您喜欢的图像作为锁屏背景。 本文运行环境:Surface P…

    2026年9月22日
    000
  • VSCode配置GDB调试器 深入掌握VSCode调试C程序技巧

    配置vscode中gdb调试c程序的核心是正确设置tasks.json和launch.json;2. tasks.json负责使用gcc -g编译生成带调试信息的可执行文件,确保prelaunchtask与launch.json中的program路径一致;3. launch.json指定调试器gdb…

    2026年9月22日
    100
  • 谷歌浏览器安卓版如何清除数据_安卓版Chrome应用数据清理方法

    首先清除浏览数据可解决谷歌浏览器页面加载慢、自动填充错误等问题。通过Chrome设置菜单可一次性清除指定时间范围内的历史记录、Cookie及缓存;针对特定网站问题,可仅清除该站点的数据以保留其他登录状态;若问题严重,可通过手机系统设置中的应用管理清除Chrome的缓存或全部数据,以重置应用状态。 如…

    2026年9月22日
    000
  • 如何在Linux命令行中进行文件查找?

    最常用Linux查找文件方法是使用find命令。按名称搜索用-name选项,如find . -name “*.log”;忽略大小写用-iname;按类型查用-type f(文件)或d(目录);按大小查用-size,如+100M表示大于100MB;按修改时间用-mtime,-7…

    2026年9月22日
    000
  • 华为天际通Go将支持eSIM:设备在路上了

    华为天际通Go将支持eSIM:设备在路上了华为天际通Go将支持eSIM:设备在路上了华为天际通Go将支持eSIM:设备在路上了华为天际通Go将支持eSIM:设备在路上了

    9月3日消息,今年的iphone 17 air将仅支持esim,彻底移除实体sim卡槽结构。随着新品发布日期的临近,国内esim政策的进展也愈发引人关注。 然而综合多方信息来看,iPhone 17 Air国行版本可能无法赶上首发,因前期在国内无法使用eSIM服务,导致该机型短期内难以在国内上市。 相…

    2026年9月22日 用户投稿
    000

发表回复

登录后才能评论
关注微信