源本科技 | 码上会

Java UDP 广播与组播

2026/03/04
16
0

UDP 的三种通信模式

UDP(用户数据报协议)凭借其无连接的特性,支持灵活多样的通信模式。根据数据包发送目标的不同,主要分为以下三种:

  1. 单播 (Unicast)

    • 定义:一对一通信。发送方将数据包发送给特定的单个接收方。

    • 场景:普通的客户端 - 服务器通信(如 DNS 查询、即时消息)。

    • 特点:目标地址是具体的单播 IP。

  2. 广播 (Broadcast)

    • 定义:一对所有通信。发送方将数据包发送给当前局域网网段内的所有主机。

    • 场景:局域网设备发现(如打印机搜索)、DHCP 请求、局域网聊天室公告。

    • 特点:目标地址是特殊的广播地址(如 255.255.255.255),路由器通常不转发广播包,限制在本地子网。

  3. 组播 (Multicast)

    • 定义:一对多通信。发送方将数据包发送给加入特定组播组的一组主机。

    • 场景:视频会议、IPTV 直播、股票行情推送、多人在线游戏状态同步。

    • 特点:目标地址是组播 IP 地址224.0.0.0 ~ 239.255.255.255),只有主动“加入”该组的主机才能收到数据,支持跨路由传输(需网络设备支持)。


UDP 广播

核心原理

广播是将数据包发送到局域网内的每一台设备。

  • 广播地址:IPv4 中,255.255.255.255 代表受限广播地址,表示当前子网内的所有主机。也可以计算特定子网的定向广播地址(如 192.168.1.255)。

  • 接收条件:接收端程序必须绑定与发送端指定的相同端口,且操作系统防火墙允许该端口的 UDP 流量。

  • 网络限制:广播包通常无法跨越路由器,只能在同一个物理或逻辑子网(VLAN)内传播。

Java 实现要点

  • 开启广播权限:默认情况下,DatagramSocket 不允许发送广播。必须调用 socket.setBroadcast(true) 显式开启。

  • 目标地址:使用 InetAddress.getByName("255.255.255.255")

局域网广播发送

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPBroadcaster {
    public static void main(String[] args) {
        try {
            int port = 9999; // 约定好的监听端口

            // 1. 创建 DatagramSocket (发送端通常不需要绑定特定端口)
            DatagramSocket socket = new DatagramSocket();

            // 2. 【关键】允许发送广播包
            socket.setBroadcast(true);

            // 3. 准备消息
            String message = "Hello Everyone! This is a LAN Broadcast.";
            byte[] data = message.getBytes("UTF-8");

            // 4. 设置目标地址为广播地址
            InetAddress broadcastAddress = InetAddress.getByName("255.255.255.255");

            // 5. 封装数据包
            DatagramPacket packet = new DatagramPacket(data, data.length, broadcastAddress, port);

            // 6. 发送
            socket.send(packet);
            System.out.println("广播消息已发送至端口 " + port + ": " + message);

            // 7. 关闭
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:接收端代码与普通 UDP 接收代码完全一致,只需绑定相同的端口(本例为 9999)即可收到广播消息。


UDP 组播

核心原理

组播是一种高效的“一对多”传输方式,介于单播和广播之间。

  • 组播地址范围224.0.0.0239.255.255.255 (D 类地址)。

    • 224.0.0.0 ~ 224.0.0.255:预留组播地址,用于本地网络协议(如 OSPF, RIP),路由器不转发。

    • 224.0.1.0 ~ 238.255.255.255:全球范围可用的组播地址。

    • 239.0.0.0 ~ 239.255.255.255:本地管理范围组播地址(类似私有 IP),常用于内部网络。

  • 加入机制:接收方必须显式地加入 (Join) 某个组播组,网卡才会接收发往该组 IP 的数据包。

  • 优势:相比广播,组播减少了网络拥塞,因为未加入组的主机不会处理数据包;相比单播,服务器只需发送一份数据,由网络设备负责复制分发。

Java 实现要点

  • 专用类:接收端必须使用 MulticastSocketDatagramSocket 的子类),因为它提供了加入和离开组播组的方法。

  • 发送端:可以使用普通的 DatagramSocket,也可以使用 MulticastSocket。只需将目标 IP 设置为组播 IP 即可。

  • 生命周期:接收端在退出前应调用 leaveGroup() 离开组,以便通知路由器停止转发数据。

组播发送与接收

发送端

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPMulticastSender {
    public static void main(String[] args) {
        try {
            int port = 8888;
            // 定义一个组播地址 (例如:224.0.0.1 是本地所有主机组)
            // 实际开发建议使用 239.x.x.x 范围内的地址以避免冲突
            String multicastIp = "224.0.0.1"; 

            // 1. 创建 Socket (普通 DatagramSocket 即可发送组播)
            DatagramSocket socket = new DatagramSocket();

            // 2. 准备数据
            String message = "Multicast Message: Stock Price Update - AAPL $150";
            byte[] data = message.getBytes("UTF-8");

            // 3. 解析组播地址
            InetAddress group = InetAddress.getByName(multicastIp);

            // 4. 封装数据包 (目标是组播 IP)
            DatagramPacket packet = new DatagramPacket(data, data.length, group, port);

            // 5. 发送
            socket.send(packet);
            System.out.println("组播消息已发送至组 " + multicastIp + ": " + message);

            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接收端

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class UDPMulticastReceiver {
    public static void main(String[] args) {
        try {
            int port = 8888;
            String multicastIp = "224.0.0.1";

            // 1. 创建 MulticastSocket 并绑定端口
            MulticastSocket socket = new MulticastSocket(port);

            // 2. 【关键】加入组播组
            InetAddress group = InetAddress.getByName(multicastIp);
            socket.joinGroup(group);
            System.out.println("已加入组播组: " + multicastIp + ",开始监听...");

            byte[] buffer = new byte[1024];

            // 3. 循环接收
            while (true) {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet); // 阻塞等待

                String msg = new String(packet.getData(), 0, packet.getLength(), "UTF-8");
                System.out.println("收到组播消息 [" + packet.getAddress().getHostAddress() + "]: " + msg);
                
                // 简单演示,实际应用中可添加退出条件
                if ("exit".equals(msg)) break; 
            }

            // 4. 离开组播组并关闭 (通常在 finally 块或 shutdown hook 中执行)
            socket.leaveGroup(group);
            socket.close();
            System.out.println("已离开组播组,连接关闭。");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  1. 防火墙拦截

    • 无论是广播还是组播,如果操作系统的防火墙(如 Windows Defender, iptables)拦截了 UDP 端口,消息将无法接收。开发测试时请确保放行对应端口的 UDP 流量。

  2. 虚拟机网络模式

    • 在使用 VMware 或 VirtualBox 运行多台虚拟机测试广播 / 组播时,务必将网络适配器设置为 “桥接模式 (Bridged)”

    • 若使用 “NAT 模式”,虚拟机处于不同的虚拟子网后,无法接收到宿主机的广播包,也无法相互通信。

  3. TTL (Time To Live)

    • 对于组播,可以通过 MulticastSocket.setTimeToLive(int ttl) 设置数据包的生命周期。

    • ttl=0:仅限本机。

    • ttl=1:仅限本地子网(默认值)。

    • ttl>1:可以跨越路由器传输到更远的网络。

  4. 组播地址选择

    • 避免使用 224.0.0.0 ~ 224.0.0.255 之间的地址,这些是保留给路由协议使用的。

    • 推荐在开发中使用 239.255.0.0 ~ 239.255.255.255 范围内的地址,这些是本地管理范围的组播地址,不会泄露到公网,较为安全。

  5. 资源释放

    • 使用 MulticastSocket 时,程序退出前务必调用 leaveGroup()。虽然关闭 Socket 通常也会隐式离开组,但显式调用能更快地通知网络设备更新转发表,提高网络效率。