1. 通信方式分类

在当前网络通信中,主要存在三种通信模式:单播广播多播(组播)。其中多播出现时间最晚,同时兼具单播和广播的优点。

  • 单播 (Unicast):单台主机与单台主机之间的通信。
  • 广播 (Broadcast):单台主机与网络中的所有主机通信。
  • 多播 (Multicast):单台主机与选定的一组主机通信。

2. 单播 (Unicast)

单播是网络通信中最常见的模式,网络节点之间的通信就好像人们之间的对话。如果一个人对另外一个人说话,用网络术语描述即为“单播”,此时信息的接收和传递只在两个节点之间进行。

2.1 单播的优点

  1. 服务器能够响应客户端的请求。
  2. 服务器能针对每个客户端的不同请求发送不同的响应,容易实现个性化服务。

2.2 单播的缺点

服务器需要针对每个客户机发送数据流。服务器总流量 = 客户机数量 × 客户机流量。在客户数量大、每个客户机流量大的流媒体应用中,服务器容易不堪重负。

2.3 应用场景

单播在网络中得到了广泛的应用,网络上绝大部分的数据都是以单播的形式传输的。例如:收发电子邮件、浏览网页时,必须与邮件服务器、网站服务器建立连接,此时使用的就是单播通信方式。

3. 广播 (Broadcast)

“广播”可以比喻为:一个人通过广播喇叭对在场的全体说话(不管对方是否乐意听)。换句话说,广播是一台主机对某一个网络上的所有主机发送数据报包。这个网络可能是整个网络,也可能是子网,还有可能是所有子网。

广播主要分为两类:本地广播定向广播

  • 定向广播:将数据报包发送到本网络之外的特定网络的所有主机。由于互联网上的大部分路由器都不转发定向广播消息,此处不再深入介绍。
  • 本地广播:将数据报包发送到本地网络的所有主机。IPv4 的本地广播地址为 255.255.255.255,路由器不会转发此广播。

3.1 广播的优点

  1. 通信效率高,信息可以一次性传递到某一个网络上的所有主机。
  2. 服务器不用向每个客户端单独发送数据,因此服务器负载较低。

3.2 广播的缺点

  1. 非常占用网络带宽。
  2. 缺乏针对性,不管主机是否真的需要接收该数据,都会强制接收。

3.3 应用场景

有线电视就是典型的广播型网络。

Java 广播示例:

客户端发送程序

// 发送端程序
public class BroadcastTest {
    public static void main(String[] args) {
        // 广播的实现:由客户端发出广播,服务器端接收
        String host = "255.255.255.255"; // 广播地址
        int port = 9999; // 广播的目的端口
        String message = "test"; // 用于发送的字符串
        try {
            InetAddress adds = InetAddress.getByName(host);
            DatagramSocket ds = new DatagramSocket();
            DatagramPacket dp = new DatagramPacket(message.getBytes(), message.length(), adds, port);
            ds.send(dp);
            ds.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务器端接收程序

// 服务器端接收程序
public class BroadcastServer {
    public static void main(String[] args) {
        int port = 9999; // 开启监听的端口
        DatagramSocket ds = null;
        DatagramPacket dp = null;
        byte[] buf = new byte[1024]; // 存储发来的消息
        StringBuffer sbuf = new StringBuffer();
        try {
            // 绑定端口
            ds = new DatagramSocket(port);
            dp = new DatagramPacket(buf, buf.length);
            System.out.println("监听广播端口打开:");
            ds.receive(dp);
            ds.close();
            int i;
            for (i = 0; i < 1024; i++) {
                if (buf[i] == 0) {
                    break;
                }
                sbuf.append((char) buf[i]);
            }
            System.out.println("收到广播消息:" + sbuf.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4. 多播/组播 (Multicast)

“组播”可以比喻为:你对着大街喊:“是男人的来一下,一人发一百块”。那么男的会过来,女的就不会过来,因为没有钱发她不理你(组播中所有的男生就是一个组)。

换句话说,组播是一台主机向指定的一组主机发送数据报包。如果采用单播方式,逐个节点传输,有多少个目标节点就会有多少次传送过程,效率极低;如果采用不区分目标、全部发送的广播方式,虽然一次可以传送完数据,但达不到区分特定数据接收对象的目的,且会占用网络带宽。采用组播方式,既可以实现一次传送所有目标节点的数据,也可以达到只对特定对象传送数据的目的。

IP 网络的组播一般通过组播 IP 地址来实现。组播 IP 地址就是 D 类 IP 地址,即 224.0.0.0239.255.255.255 之间的 IP 地址。

4.1 组播的优点

  1. 具备广播所具备的所有优点。
  2. 与单播相比,提高了发送数据报包的效率;与广播相比,减少了网络流量。

4.2 组播的缺点

  1. 与单播协议相比没有纠错机制,发生丢包错包后难以弥补,但可以通过一定的容错机制和 QoS 加以弥补。

组播的简单示例:

客户端发送消息

// 发送端程序
public class SendUdp {
    public static void main(String[] args) throws IOException {
        MulticastSocket ms = null;
        DatagramPacket dataPacket = null;
        ms = new MulticastSocket();
        ms.setTimeToLive(32);
        // 将本机的 IP 地址放到数据包里,其实 server 端接收到数据包后也能获取到发包方的 IP 的
        byte[] data = "组播 测试".getBytes();
        InetAddress address = InetAddress.getByName("239.0.0.255");
        dataPacket = new DatagramPacket(data, data.length, address, 8899);
        ms.send(dataPacket);
        ms.close();
    }
}

服务器端接收程序

// 服务器端程序
public class TestMain {
    private static MulticastSocket ds;
    static String multicastHost = "239.0.0.255";
    static InetAddress receiveAddress;

    public static void main(String[] args) throws IOException {
        ds = new MulticastSocket(8899);
        receiveAddress = InetAddress.getByName(multicastHost);
        ds.joinGroup(receiveAddress);
        new Thread(new udpRunnable(ds)).start();
    }
}

class udpRunnable implements Runnable {
    MulticastSocket ds;

    public udpRunnable(MulticastSocket ds) {
        this.ds = ds;
    }

    public void run() {
        byte buf[] = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, 1024);
        while (true) {
            try {
                ds.receive(dp);
                System.out.println("receive client message : " + new String(buf, 0, dp.getLength()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果截图:

7137212015122410113370250849467.png

5. 参考资料

  1. http://www.2cto.com/kf/201312/264488.html

说明

  • 代码版本:文中示例代码采用较早的 Java 风格(未使用 try-with-resources 语法),适用于理解原理,实际生产环境建议结合 Java 7+ 特性优化资源管理。
  • 网络环境:多播/组播功能依赖网络设备(路由器、交换机)的支持,部分局域网或云服务器环境可能默认禁止组播流量,测试时请确保网络配置允许。