文档

Java™ 教程-Java Tutorials 中文版
广播给多个收件人
Trail: Custom Networking
Lesson: All About Datagrams

广播给多个收件人

除了允许程序相互发送数据包的 DatagramSocket 之外,java.net 还包含一个名为 MulticastSocket 的类。这种套接字在客户端用于侦听服务器向多个客户端广播的数据包。

让我们重写引语服务器,以便它向多个收件人广播 DatagramPacket。新服务器现在需要定期广播引语,而不是向发出请求的特定客户端发送引语。需要修改客户端,以便被动地侦听引语,并在 MulticastSocket 上进行修改。

此示例由三个类组成,这三个类是前一个示例中三个类的修改:MulticastServerMulticastServerThread,和 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,特别是用于构造 DatagramPacketInetAddress 和端口。回想一下,前面的示例从客户端发送到服务器的数据包中获取了 InetAddress 和端口号。这是因为服务器需要直接回复客户端。现在,服务器需要解决多个客户端。所以这次 InetAddress 和端口号都是硬编码的。

硬编码端口号为 4446(客户端必须具有绑定到此端口的 MulticastSocket)。DatagramPacket 的硬编码 InetAddress 是“203.0.113.0”并且是组标识符(而不是运行单个客户端的计算机的 Internet 地址)。此特定地址是为保留此目的而从保留中任意选择的。

以这种方式创建,DatagramPacket 的目的地是所有侦听端口号为 4446 的客户端,这些客户端是“203.0.113.0”组的成员。

要侦听端口号 4446,新客户端程序刚刚使用该端口号创建了 MulticastSocket。要成为“203.0.113.0”组的成员,客户端使用标识组的 InetAddress 调用 MulticastSocketjoinGroup 方法。现在,客户端设置为接收指定给指定端口和组的 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 和几个客户端。观察客户如何获得相同的引语。



Previous page: Writing a Datagram Client and Server
Next page: Programmatic Access to Network Parameters