UDP广播和组播

这篇笔记我们继续学习如何使用Go语言实现UDP广播和组播。

UDP广播

UDP广播允许将UDP数据包发送到子网内的所有设备。广播使用的IP地址比较特殊,我们需要将发送UDP数据包的目标IP设置为255.255.255.255,除此之外Go语言代码其实和我们之前编写的UDP客户端和服务端是类似的。我们向这个地址发送UDP数据包后所有监听指定UDP端口的网络设备都会收到UDP包。

下面例子代码中,UDP发送端向接收端发送一条信息,随后结束程序。广播UDP包接收端代码如下。

package main

import (
    "log/slog"
    "net"
)

func main() {
    // UDP监听8000端口
    addr, err := net.ResolveUDPAddr("udp", ":8000")
    if err != nil {
        slog.Error(err.Error())
        return
    }
    conn, err := net.ListenUDP("udp", addr)
    if err != nil {
        slog.Error(err.Error())
        return
    }
    defer func(conn *net.UDPConn) {
        err := conn.Close()
        if err != nil {
            slog.Error(err.Error())
            return
        }
    }(conn)
    for {
        // 打印收到的广播消息
        buffer := make([]byte, 512)
        n, addr, err := conn.ReadFromUDP(buffer)
        if err != nil {
            slog.Error(err.Error())
            continue
        }
        data := string(buffer[:n])
        slog.Info("[" + addr.String() + "] " + data)
    }
}

广播UDP包发送端代码如下。

package main

import (
    "log/slog"
    "net"
)

func main() {
    // 使用广播地址准备UDP套接字
    addr, err := net.ResolveUDPAddr("udp", "255.255.255.255:8000")
    if err != nil {
        slog.Error(err.Error())
        return
    }
    conn, err := net.DialUDP("udp", nil, addr)
    if err != nil {
        slog.Error(err.Error())
        return
    }
    defer func(conn *net.UDPConn) {
        err := conn.Close()
        if err != nil {
            slog.Error(err.Error())
            return
        }
    }(conn)
    // 发送Ping字符串
    _, err = conn.Write([]byte("Ping"))
    if err != nil {
        slog.Error(err.Error())
        return
    }
}

这里我们要注意,UDP广播包是单向发送的,我们只能发送UDP包而接收端不能直接回复,不过接收端收到包后可以得到发送端的IP地址,因此接收端可以使用单播的方式向原发送端主动发送消息来回复。实际开发中,广播UDP包最常见的用途就是获取内网中程序的上线列表,我们通过一个程序节点发送UDP广播包,其它程序节点通过单播回复自己的IP地址和上线状态。

UDP组播

UDP组播(也叫多播)是指将UDP包发送到特定群组的设备。组播也不难理解,接收端需要绑定一个组播地址,这类似于加入一个“聊天群”,发送端向组播地址发送UDP包时,所有加入群的设备都会收到这条“群消息”。组播需要路由器的支持,组播地址的范围是224.0.0.0239.255.255.255

下面代码中,我们改写前面UDP广播的例子代码,实现UDP组播。接收端代码如下。

package main

import (
    "log/slog"
    "net"
)

func main() {
    //UDP绑定组播8000端口
    addr, err := net.ResolveUDPAddr("udp", "224.0.2.0:8000")
    if err != nil {
        slog.Error(err.Error())
        return
    }
    conn, err := net.ListenMulticastUDP("udp", nil, addr)
    if err != nil {
        slog.Error(err.Error())
        return
    }
    defer func(conn *net.UDPConn) {
        err := conn.Close()
        if err != nil {
            slog.Error(err.Error())
            return
        }
    }(conn)
    for {
        // 打印收到的组播消息
        buffer := make([]byte, 512)
        n, addr, err := conn.ReadFromUDP(buffer)
        if err != nil {
            slog.Error(err.Error())
            continue
        }
        data := string(buffer[:n])
        slog.Info("[" + addr.String() + "] " + data)
    }
}

注意代码中net.ListenMulticastUDP()函数用于监听组播地址,它有3个参数,第1个参数指定UDP协议;第2个参数是网络接口,我们可以用类似net.InterfaceByName("eth0")的写法指定网络接口,如果传nil则表示使用系统默认的组播网络接口;第3个参数是组播地址,一般来说224.0.2.0~238.255.255.255是用户可用的组播地址,其他组播地址可能被保留作为其他用途,因此路由器未必支持使用其它的地址进行UDP组播。

UDP组播发送端代码如下。

package main

import (
    "log/slog"
    "net"
)

func main() {
    // 使用组播地址准备UDP套接字
    addr, err := net.ResolveUDPAddr("udp", "224.0.2.0:8000")
    if err != nil {
        slog.Error(err.Error())
        return
    }
    conn, err := net.DialUDP("udp", nil, addr)
    if err != nil {
        slog.Error(err.Error())
        return
    }
    defer func(conn *net.UDPConn) {
        err := conn.Close()
        if err != nil {
            slog.Error(err.Error())
            return
        }
    }(conn)
    // 发送Ping字符串
    _, err = conn.Write([]byte("Ping"))
    if err != nil {
        slog.Error(err.Error())
        return
    }
}

发送端代码没有什么特别的,我们向组播地址发送UDP包,发送后会观察到加入组播的所有接收端都受到了UDP包。如果没有收到UDP包也不用惊慌,可能是路由器配置不支持组播,我们知道有UDP组播这样一个重要的机制即可。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap