Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
除了允许程序相互发送数据包的 DatagramSocket
之外,java.net 还包含一个名为 MulticastSocket
的类。这种套接字在客户端用于侦听服务器向多个客户端广播的数据包。
让我们重写引语服务器,以便它向多个收件人广播 DatagramPacket
。新服务器现在需要定期广播引语,而不是向发出请求的特定客户端发送引语。需要修改客户端,以便被动地侦听引语,并在 MulticastSocket
上进行修改。
此示例由三个类组成,这三个类是前一个示例中三个类的修改:MulticastServer
,MulticastServerThread
,和 MulticastClient
。本讨论重点介绍了这些类的有趣部分。
这是服务器主程序的新版本。此代码与先前版本 QuoteServer
之间的差异以粗体显示:
import java.io.*; public class MulticastServer { public static void main(String[] args) throws IOException { new MulticastServerThread().start(); } }
基本上,服务器获得一个新名称并创建 MulticastServerThread
而不是 QuoteServerThread
。现在让我们看一下包含服务器核心的 MulticastServerThread
。这是它的类声明:
public class MulticastServerThread extends QuoteServerThread { // ... }
我们已经使这个类成为 QuoteServerThread
的子类,以便它可以使用构造函数,并继承一些成员变量和 getNextQuote
方法。回想一下 QuoteServerThread
创建一个绑定到端口 4445 的 DatagramSocket
并打开引语文件。在这个例子中,DatagramSocket
的端口号实际上并不重要,因为客户端从不向服务器发送任何内容。
MulticastServerThread
中唯一明确实现的方法是 run
方法。此 run
方法与 QuoteServerThread
中的方法之间的差异以粗体显示:
public void run() { while (moreQuotes) { try { byte[] buf = new byte[256]; // don't wait for request...just send a quote String dString = null; if (in == null) dString = new Date().toString(); else dString = getNextQuote(); buf = dString.getBytes(); InetAddress group = InetAddress.getByName("203.0.113.0"); DatagramPacket packet; packet = new DatagramPacket(buf, buf.length, group, 4446); socket.send(packet); try { sleep((long)Math.random() * FIVE_SECONDS); } catch (InterruptedException e) { } } catch (IOException e) { e.printStackTrace(); moreQuotes = false; } } socket.close(); }
有趣的变化是如何构造 DatagramPacket
,特别是用于构造 DatagramPacket
的 InetAddress
和端口。回想一下,前面的示例从客户端发送到服务器的数据包中获取了 InetAddress
和端口号。这是因为服务器需要直接回复客户端。现在,服务器需要解决多个客户端。所以这次 InetAddress
和端口号都是硬编码的。
硬编码端口号为 4446(客户端必须具有绑定到此端口的 MulticastSocket
)。DatagramPacket
的硬编码 InetAddress
是“203.0.113.0”并且是组标识符(而不是运行单个客户端的计算机的 Internet 地址)。此特定地址是为保留此目的而从保留中任意选择的。
以这种方式创建,DatagramPacket
的目的地是所有侦听端口号为 4446 的客户端,这些客户端是“203.0.113.0”组的成员。
要侦听端口号 4446,新客户端程序刚刚使用该端口号创建了 MulticastSocket
。要成为“203.0.113.0”组的成员,客户端使用标识组的 InetAddress
调用 MulticastSocket
的 joinGroup
方法。现在,客户端设置为接收指定给指定端口和组的 DatagramPacket
。这是来自新客户端程序的相关代码(也被重写为被动接收引语而不是主动请求它们)。粗体语句是与 MulticastSocket
交互的语句:
MulticastSocket socket = new MulticastSocket(4446); InetAddress group = InetAddress.getByName("203.0.113.0"); socket.joinGroup(group); DatagramPacket packet; for (int i = 0; i < 5; i++) { byte[] buf = new byte[256]; packet = new DatagramPacket(buf, buf.length); socket.receive(packet); String received = new String(packet.getData()); System.out.println("Quote of the Moment: " + received); } socket.leaveGroup(group); socket.close();
请注意,服务器使用 DatagramSocket
来广播客户端通过 MulticastSocket
接收的数据包。或者,它可以使用 MulticastSocket
。服务器用于发送 DatagramPacket
的套接字并不重要。广播数据包时重要的是 DatagramPacket
中包含的寻址信息,以及客户端用来监听它的套接字
运行 MulticastServer
和几个客户端。观察客户如何获得相同的引语。