4.3.2 多播
与广播一样,多播与单播之间的一个主要区别是地址的形式。一个多播地址指示了一组接收者。IP协议的设计者为多播分配了一定范围的地址空间,IPv4中的多播地址范围是224.0.0.0到239.255.255.255,IPv6中的多播地址是任何由FF开头的地址。除了少数系统保留的多播地址外,发送者可以向以上范围内的任何地址发送数据。Java中多播应用程序主要通过MulticastSocket实例进行通信,它是DatagramSocket的一个子类。重点需要理解的是,一个MulticastSocket实例实际上就是一个UDP套接字(DatagramSocket),其包含了一些额外的可以控制的多播特定属性。我们的下一个例子实现了投票信息的多播发送者和接收者。
VoteMulticastSender.java
我们的单播发送者和多播发送者仅有的重要区别是:1)对给定地址是否是多播地址进行了验证,2)为多播数据报文设置了初始的TTL值(Time To Live,生命周期)。每个IP数据报文中都包含了一个TTL,它被初始化为某个默认值,并在每个路由器转发该报文时递减(通常是减1)。当TTL值减为0时,就丢弃该数据报文。通过设置TTL的初始值,我们可以限制数据包从发送者开始所能传递到的最远距离。[1]
与广播不同,网络多播只将消息副本发送给指定的一组接收者。这组接收者叫做多播组(multicast group),通过共享的多播(组)地址确定。接收者需要一种机制来通知网络它对发送到某一特定地址的消息感兴趣,以使网络将数据包转发给它。这种通知机制叫做加入一组(joining a group),可以由MulticastSocket类的joinGroup()方法实现。我们的多播接收者加入了一个特定的组,接收并打印该组的一条多播消息,然后退出。
VoteMulticastReceiver.java
我们的多播和单播接收者唯一的重要区别是,多播接收者表明希望从哪个多播地址接收数据来加入多播组。本书的网站上还有另一个多播发送者和接收者的例子。MulticastImageSender.java将一组由命令行参数指定的图片(JPEG或GIF)以3秒的时间间隔传输。MulticastImageReceiver.java则接收每一个图片并在窗口中显示。
多播数据报文实际上可以通过DatagramSocket中发送,只需要简单地指定一个多播地址。不过MulticastSocket还有一些DatagramSocket没有的能力,包括1)允许指定数据报文的TTL,2)允许指定和改变通过哪个接口将数据报文发送到组(接口由其互联网地址确定)。另一方面,一个多播消息接收者必须使用MulticastSocket来接收数据,因为它需要用到MulticastSocket加入组的功能。
MulticastSocket是DatagramSocket的一个子类,因此它提供了DatagramSocket的全部方法。在此,我们只对MulticastSocket中特有或修改过的方法进行介绍。
MulticastSocket:创建
这些构造函数用来创建一个具有多播功能的UDP套接字。如果没有指定本地端口或指定为0,套接字将绑定到任意一个可以用的本地端口。如果指定了地址,该套接字则只能从所指定的地址接收消息。
如果要接收数据报文,我们必须加入到一个多播组中。
MulticastSocket:组管理
joinGroup()和leaveGroup()方法用于管理该套接字的多播组成员资格信息。一个套接字可以同时为多个多播组成员。如果套接字加入一个已经加入了的组,或在没有加入任何组的情况下离开一个组,都可能抛出异常。还可以选择性地指定从哪个接口加入或离开多播组。
MulticastSocket:设置/获取多播选项
getTimeToLive()和setTimeToLive()方法用于设置通过该套接字发送的所有数据报文的生存周期。如果套接字启用了回环模式,则会接收到自己发送的数据报文。getLoopbackMode()和setLoopbackMode()方法用于为多播套接字设置回环模式,将其设置为true时表示关闭回环模式。getInterface()方法,setInterface()方法,getNetworkInterface()以及setNetworkInterface()方法用于设置从哪个接口发送多播数据包。这主要用在有多个接口的主机上。默认的多播接口是与平台独立的。
决定使用广播还是使用多播需要考虑多方面的因素,包括接收者的网络地址和通信双方的知识。互联网广播的范围是限定在一个本地广播网络之内的,并对广播接收者的位置进行了严格的限制。多播通信可能包含网络中任何位置的接收者[2],因此多播有个好处就是它能够覆盖一组分布在各处的接收者。IP多播的不足在于接收者必须知道要加入的多播组的地址。而接收广播信息则不需要指定地址信息。在某些情况下,广播是一个比多播更好更易于发现的机制。所有主机在默认情况下都可以接收广播,因此,在一个网络中向所有主机询问“打印机在哪儿?”是件容易的事。
UDP单播、多播和广播都基于底层UDP套接字实现。这些实现的大部分语义都是,一个UDP数据报文将发送到所有与数据包的目标端口绑定的套接字上。也就是说,如果有一个DatagramSocket或MulticastSocket实例与本地端口X相绑定(没有指定本地地址,即野报文),地址为Y的主机可能会在以下几种情况下收到发向端口X的UDP数据报文:1)目的地址为Y的单播,2)主机Y上所有应用程序都加入了发送到的多播组,或3)向能够到达主机Y的网络进行广播。接收者可以使用connect()方法来限定数据报文的源地址和端口。同时,一个DatagramSocket实例也可以指定本地单播地址,这将阻止多播和广播数据包的转发。目的地址验证的例子见UDPEchoClientTimeout.java,数据报文分成多路处理的详情见第6.5节。
[1]实际上多播的TTL规则并不是这么简单。一个TTL=4的数据包并不是一定会从发送开始传递4跳,但是它传递的距离不能超过4跳。
[2]在写本书的过程中,对于谁可以接收互联网上的多播信息也有严格限制。只要当发送者和接收者在同一个LAN中时才能使用多播。