【问题标题】:MulticastSocket constructors and binding to port or SocketAddressMulticastSocket 构造函数和绑定到端口或 SocketAddress
【发布时间】:2013-10-23 21:41:30
【问题描述】:

我可能对这里的绑定术语有一个基本的误解,但我对MulticastSocket 及其构造函数的用法感到困惑。他们不会不做理解他们应该做的事情应该这样做任何可以帮助我消除误解的人将不胜感激。

首先是我想要实现的目标。我试图编写一个简短的程序,创建一个 MulticastSocket 将它绑定(即监听)到一个特定的网络适配器,然后加入一个特定的多播组。我已经尝试过以下(客户端)代码,它可以正常工作,我可以向它多播一个数据包,而多播套接字不会超时。

public class Main {
public static final int DEFAULT_MULTICAST_PORT = 5555;
public static final String multicastGroup = "225.4.5.6";
public static final String adapterName = "eth0";
public static final int MAX_PACKET_SIZE = 65507;

CharBuffer charBuffer = null;
Charset charset = Charset.defaultCharset();
CharsetDecoder decoder = charset.newDecoder();
static ByteBuffer message = ByteBuffer.allocateDirect(MAX_PACKET_SIZE);
static boolean loop = true;

static byte[] buffer = new byte[MAX_PACKET_SIZE];

public static void main(String[] args) {

    try {
        //MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));
        DatagramPacket p = new DatagramPacket(buffer, MAX_PACKET_SIZE);
        while (loop){
            try{
                mSocket.receive(p);
                System.out.println("Packet Received.");
            } catch (SocketTimeoutException ex){
                System.out.println("Socket Timed out");
            }
        }

    } catch (IOException ex){
        System.err.println(ex);
    }

}

}

不幸的是,一旦我将 MulticastSocket 构造函数更改为MulticastSocket(SocketAddress bindaddr),它就会停止工作。看来我只能使用绑定到端口的构造函数来工作,所以当调用这个构造函数时它到底绑定到什么,因为我在这个阶段没有指定网络适配器。 (我知道我稍后会使用特定的 NetworkInterface 加入该组,但我如何确定在构造函数调用期间它不会绑定到任何适配器?)

我也可以在不指定适配器的情况下加入一个组,然后我不知道它绑定到哪个适配器。

谁能解释绑定到端口 only 的实际作用,是否可以只在特定的 NetworkInterface 上进行监听?

更新 #1 **

看了目前的回复,和同事讨论过,以下是我对Java MulticastSocket的理解:

  1. MulticastSocket() 创建一个多播套接字,该套接字绑定一个随机选择的端口(由主机的底层操作系统绑定到通配符地址 0.0.0.0 即所有网卡。但是使用 null 调用此构造函数会创建一个未绑定的 MulticastSocket。在这种情况下,调用 `bind(SocketAddress) 方法会绑定到 IP 和端口。
  2. MulticastSocket(int port) 创建一个绑定到特定端口但在每个 IP 地址上的多播套接字。
  3. MulticastSocket(SocketAddress sa) 创建一个绑定到指定 IP 地址(可以是任何 IP 地址,甚至无效的多播地址)和端口的多播套接字。

使用选项 2,这意味着可能任何发送到指定端口的数据包,无论其实际目的地如何,都将被传递到 MulticastSocket。我说可能是因为组播数据包只有在组已加入时才会到达(但其他发往非组播地址的数据包将到达如果端口号匹配?)

使用选项 3,我可以绑定到一个 IP 地址,并且 只有 目的地匹配的数据包将到达套接字。使用此选项绑定到特定网络接口的 IP 是完全可行的,但是不会收到多播数据包,因为它们不会发往网卡的特定 IP 地址(这就是为什么我从未看到它们到达代码示例)。也可以绑定到一个有效的多播地址,但在这种情况下只有目的地与绑定的多播地址匹配的特定数据包会到达套接字,不管对joinGroup()的调用 em>。

现在对joinGroup() 的调用对套接字本身什么都不做,而是向底层网络系统发出一个 IGMP 请求,以确保路由器、操作系统本身等真正开始路由 em> 指定的多播数据包通过硬件并通过网络堆栈,最后到达 Java MulticastSocket 本身。

** 更新 2 ** 引自“UNIX 网络编程”,Stevens, Fenner, Rudoff:

要接收多播数据报,进程必须加入多播 组,它还必须绑定一个 UDP 套接字到 prot 号 将用作发送到的数据报的目标端口号 团体。这两个操作是不同的,两者都是必需的。加盟 该组告诉主机的IP层和数据链路层接收 多播数据报发送到该组。绑定端口是怎么回事 应用程序向 UDP 指定它想要接收发送的数据报 到那个港口。一些应用程序还将多播地址绑定到 套接字,除了端口。这可以防止任何其他数据报 可能会从该端口接收到其他单播、广播或 多播地址被传递到套接字。

我认为这说明了一切。

** 更新 3 ** 只是想发布我测试的代码,cmets 解释了每个代码会发生什么。

/**
         * This first creates an UNBOUND Multicast Socket and then binds to
         * a port (but accepting the wildcard IP 0.0.0.0.
         * The Following WORKS:
         */

        /*MulticastSocket mSocket = new MulticastSocket(null);
        mSocket.bind(new InetSocketAddress(DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));
        */

        /**
         * The following creates a a network socket and binds in the constructor
         * to a local adapter and port. Consequently it DOES not work because
         * it only allows destination ips that match the bound address & port
         * even though the desired group is joined.
         */

        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/


        /**
         * The following binds to the same multicast group this is 'joined' later
         * and this works correctly. However if the join() is NOT called, no packets 
         * arrive at the socket, as expected.
         */

        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT));

        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByName(adapterName);
        // Comment out the following line and it no longer workds correctly.
        mSocket.joinGroup(InetAddress.getByName(multicastGroup));*/

        /**
         * The following binds to a a specified port on 0.0.0.0 and joins 
         * a specific Multicast group on a specific adapter. This must mean that the IGMP must occur 
         * on the specified adapter.
         * 
         * ** This will ALSO receive packets addressed DIRECTLY to the ip 192.168.2.23 with the same
         * port as DEFAULT_MULTICAST_POR ***ONLY!!***
         */
        MulticastSocket mSocket = new MulticastSocket(DEFAULT_MULTICAST_PORT);
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));
        mSocket.joinGroup(new InetSocketAddress(multicastGroup, DEFAULT_MULTICAST_PORT),NetworkInterface.getByName(adapterName));

        /**
         * The following binds to a specific address and port (i.e. adapter address)
         * and then ONLY accepts UDP packets with destination equal to that IP.
         */
        /*MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
        mSocket.setReuseAddress(true);
        mSocket.setSoTimeout(5000);
        NetworkInterface nic = NetworkInterface.getByInetAddress(InetAddress.getByName("192.168.2.23"));*/

【问题讨论】:

  • 使用 SocketAddress 创建多播套接字意味着我们将套接字绑定到 SocketAddress 对吗?那么如果我们加入一个多播组,为什么套接字无法接收多播数据报????
  • 嗯,这是一个绝对零意义的句子:This will ALSO receive packets addressed DIRECTLY to the ip 192.168.2.23 with the same port as DEFAULT_MULTICAST_POR ***ONLY!!***...它怎么可能同时和仅同时。这在逻辑上是不可能的,所以我不知道你在说什么,我想知道,因为我也想了解更多。
  • @searchengine27也许如果你的回复更有礼貌,我会倾向于提供帮助。
  • 这是零意义。你真的是想为一个实际上只是陈述事实的评论打架吗?没有什么礼貌或不礼貌的——这只是一个事实。现在你太不礼貌了 :) 感谢你证明你没有能力成为社区中受人尊敬的成员 :)

标签: java multicast multicastsocket


【解决方案1】:

如果您在创建或绑定时未指定本地 IP 地址,则会绑定到 0.0.0.0,即“接受任何 NIC 的输入”。这通常是您想要的。

可以绑定到特定的 IP 地址,这隐含地表示相应的 NIC,但某些系统(例如 Linux)似乎希望多播套接字(如果绑定)绑定到多播组本身。这对我来说没有任何意义:如果你想加入另一个小组怎么办?

我认为最好和最便携的想法是在 0.0.0.0 监听并通过特定的 NIC 或通过所有 NIC 加入,一次一个。后者在多宿主主机中是必需的,除非您确信到多播组的默认路由是您希望发送加入请求的路由,因为如果您不指定加入接口,就会发生这种情况。

【讨论】:

  • 经过进一步思考,我已在问题中添加了更多信息,希望您能提出宝贵意见。
  • 如果您使用选项 3,您还应该通过该界面加入,正如我上面所说的。不过,它仍然存在接收非多播的问题。您可以尝试绑定到多播地址本身,但它可能无法在您的平台上运行,正如我在上面所说的那样。
  • EJP 我认为我们基本上同意会发生什么,但我应该指出,在我所做的 tets 中,可以将多播套接字绑定到非多播地址,这基本上可以防止任何多播数据包被接收。我现在意识到 join() 和 bind()` 是两个不同的东西。也可以使用绑定到非多播地址的多播套接字来接收发往该非多播地址的数据报。这是有道理的,因为 MulticaseSocket 只是 DatagramSocket 的一个特化。我使用允许绑定的 Linux。我不在乎 Windows。
  • 如果其他人对此进行深入思考,我将尽快发布我的测试摘要以供将来参考...
  • "它绑定到 0.0.0.0,这意味着'接受通过任何 NIC 的输入'" - 这是不正确的
【解决方案2】:

我认为您的地址绑定没有抓住重点:

http://download.java.net/jdk7/archive/b123/docs/api/java/net/MulticastSocket.html
多播组由 D 类 IP 地址和标准 UDP 端口号指定。 D 类 IP 地址的范围为 224.0.0.0 到 239.255.255.255(含)。地址 224.0.0.0 已保留,不应使用。

【讨论】:

  • 现在完全没抓住重点?乍一看,他的多播地址看起来不错。
  • @EJP 我说的是注释掉的代码,正如我所理解的那样不起作用//MulticastSocket mSocket = new MulticastSocket(new InetSocketAddress("192.168.2.23", DEFAULT_MULTICAST_PORT));
  • 德国人,感谢您的回复。你能评论我添加的附加信息吗?
  • 我没有检查流量是否确实流向了所有地址 - 可能很有趣,不幸的是我目前没有足够的计算机来检查自己。如果您可以设置并报告您的发现。
  • Germann,我没有意识到bindingjoining 完全是两个不同的东西。您可以绑定到任何 IP 地址,但您只能加入多播 IP 地址。
猜你喜欢
  • 2015-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-26
  • 1970-01-01
相关资源
最近更新 更多