网络编程总结(七):广播和多播

我们都知道,网络的通信模式可以分为单播、广播和多播:

  • 单播:点到点的通信方式
  • 多播:点到多点的通信方式
  • 汇播:多点到一点的通信方式
  • 群播:多点到多点的通信方式
  • 广播:点到所有节点的通信方式

常用的是单播、多播和广播

之前我们将的 TCP Socket 和 UDP Socket 都是点到点的,属于单播,就好比两个人交谈,交谈的内容只有他俩知道(当然也有别人偷听,这里不算)。

多播就好像是群组聊天,只有特定的人才能知道聊天内容。

广播就是大喇叭了,所有人都能听到。

广播

在 TCP/IP 协议栈中,传输层只有 UDP 可以广播,广播数据包不经过路由器,只在同一个局域网内部广播。

IPv4 的广播地址是 255.255.255.255.

广播可以使得通信的效率提高,因为它不光三七二十一,直接把数据报发送到每一个客户端,这样同样也会带来问题,那就是占用贷款,并且没有针对性,所有客户端都必须强制接收。

代码就不列了,只需要把之前的 UDP Socket 代码中的 IP 换成广播地址就 OK。

多播

多播其实和广播类似,只是多播指定了一组接受者,只有指定的接受者可以接收到数据报。

IPv4 中的多播地址范围是 224.0.0.0 到 239.255.255.255,地址 224.0.0.0 被保留,不应使用。发送者可以向以上范围内的任何地址发送数据。

Java 中多播应用程序主要通过 MulticastSocket,它是 DatagramSocket 的子类,所以说其本质还是 DatagramSocket,只是另外包含了一些额外的可以控制的多播特定属性。

构造方法:

  • MulticastSocket()
    创建多播套接字。
  • MulticastSocket(int port)
    创建多播套接字并将其绑定到特定端口。
  • MulticastSocket(SocketAddress bindaddr)
    创建绑定到指定套接字地址的 MulticastSocket。

查询方法:

  • getInterface()
    获取用于多播数据包的网络接口的地址。
  • getLoopbackMode()
    获取多播数据报的本地回送的设置。
  • getNetworkInterface()
    获取多播网络接口集合。
  • getTimeToLive()
    获取在套接字上发出的多播数据包的默认生存时间。

设置方法:

  • setInterface(InetAddress inf)
    设置多播网络接口,供其行为将受网络接口值影响的方法使用。
  • setLoopbackMode(boolean disable)
    启用/禁用多播数据报的本地回送。
  • setNetworkInterface(NetworkInterface netIf)
    指定在此套接字上发送的输出多播数据报的网络接口。
  • setTimeToLive(int ttl)
    设置在此 MulticastSocket 上发出的多播数据包的默认生存时间,以便控制多播的范围。

其他方法:

  • joinGroup(InetAddress mcastaddr)
    加入多播组。
  • joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
    加入指定接口上的指定多播组。
  • leaveGroup(InetAddress mcastaddr)
    离开多播组。
  • leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
    离开指定本地接口上的多播组。

我们都知道,UDP Socket 是不区分客户端和服务端的,只能区分接收端和发送端,MulticastSocket 又是 DatagramSocket 的子类,所以它也是一样,只要该客户端加入了多播组,那么它就能接收来自该组其他客户端发送的消息,同时也能发送消息给该组的其他客户端。

客户端通过 leaveGroup 方法脱离多播组,脱离之后,就不会再收到该组成员发送的消息了。

多播数据包有一个默认生存时间即 TTL,用以控制多播的范围,每当有路由器转发该报文时,TTL减1,直到减为0时,生命周期结束,报文即时没有到达目的地,也立即宣布死亡。该时间必须在 0~255 之间,可以通过 setTimeToLive 方法设置,通过 getTimeToLive 方法获取。

一个简单的例子:

发送端

public class MulticastSocketSender {
    public static void main(String[] args) {
        try {
            byte[] bytes = "我想要怒放的生命".getBytes();
            MulticastSocket socket = new MulticastSocket();
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("224.0.0.1"), 6789);
            socket.joinGroup(InetAddress.getByName("224.0.0.1"));
            socket.send(packet);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

接收端

public class MulticastSocketReceiver {
    public static void main(String[] args) {
        try {
            byte[] bytes = new byte[1024];
            MulticastSocket socket = new MulticastSocket(6789);
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
            socket.joinGroup(InetAddress.getByName("224.0.0.1"));
            socket.receive(packet);
            byte[] data = packet.getData();
            System.out.println(new String(data, 0, data.length));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}