Broadcast 到多个收件人

除了DatagramSocket(允许程序彼此发送数据包)之外,java.net 还包括一个名为MulticastSocket的类。这种套接字用于 Client 端,以侦听服务器 Broadcast 到多个 Client 端的数据包。

让我们重写报价服务器,以便它向多个收件人 BroadcastDatagramPacket。现在,新服务器不再需要将报价发送给发出请求的特定 Client 端,而是需要定期 Broadcast 报价。需要修改 Client 端,以便它被动地监听报价,并在MulticastSocket上监听报价。

此示例由三个类组成,这三个类是对上一个示例MulticastServerMulticastServerThreadMulticastClient的三个类的修改。本讨论重点介绍了这些类的有趣部分。

这是服务器主程序的新版本。此代码与先前版本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的端口号实际上并不重要,因为 Client 端从不向服务器发送任何内容。

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的构造方式,尤其是InetAddress和用于构造DatagramPacket的端口。回想一下,前面的示例从 Client 端发送到服务器的数据包中检索了InetAddress和端口号。这是因为服务器需要直接回复 Client 端。现在,服务器需要寻址多个 Client 端。因此,这次InetAddress和端口号都是硬编码的。

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

以这种方式创建的DatagramPacket面向所有监听端口号 4446 的“ 203.0.113.0”组成员的所有 Client 端。

要监听端口号 4446,新的 Client 端程序刚刚使用该端口号创建了其MulticastSocket。为了成为“ 203.0.113.0”组的成员,Client 端使用标识该组的InetAddress调用MulticastSocketjoinGroup方法。现在,将 Client 端设置为接收指定端口和组的DatagramPacket。这是新 Client 端程序中的相关代码(该代码也被重写为被动接收报价,而不是主动请求报价)。粗体语句是与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();

请注意,服务器使用DatagramSocketBroadcastClient 端通过MulticastSocket接收的数据包。或者,它可以使用MulticastSocket。服务器用于发送DatagramPacket的套接字并不重要。Broadcast 数据包时,重要的是DatagramPacket中包含的寻址信息以及 Client 端用来侦听它的套接字

Try this:

运行MulticastServer和几个 Client 端。观看所有 Client 如何获得相同的报价。