MulticastSocket构造函数和绑定到端口或SocketAddress [英] MulticastSocket constructors and binding to port or SocketAddress

查看:402
本文介绍了MulticastSocket构造函数和绑定到端口或SocketAddress的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可能对绑定这个术语有一个基本的误解,但我对 MulticastSocket 和它的构造函数。他们不会做明白他们应该做的事情应该这样做,任何可以帮助我清除我的误解的人都会受到赞赏。

I may have a fundamental misunderstanding of the term binding here but I am confused about the usage of the MulticastSocket and it's constructors. They no not do what I understand they should do should do so any who can help me clear my misunderstanding would be appreciated.

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

Firstly what I am trying to achieve. I have tried to write a short program that creates a MulticastSocket bind it (i.e. listen) on a specific network adapter and then join a specific Multicast group. I have tried the following (client) code which works ok, I can multicast a packet to it without the Multicast socket timing out.

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),它就会停止工作。似乎我只能使用bind-to-port构造函数来工作,所以当调用这个构造函数时它完全绑定到什么,因为我在这个阶段没有指定网络适配器。 (我知道我稍后会加入具有特定NetworkInterface的组,但是如何确保在构造函数调用期间它不绑定到任何适配器?)

Unfortunately as soon as I alter the MulticastSocket constructor to MulticastSocket(SocketAddress bindaddr) it stops working. It seems I can only use the bind-to-port constructor for it to work so what does it exactly bind to when this constructor is called as I have not specified a network adapter at this stage. (I know I join the group with a specific NetworkInterface later on, but how can I be sure that during the constructor call it doesn't bind to ANY adapter?)

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

I could also join a group without specifying the adapter then I don't know which adapter it's bound to.

任何人都可以解释什么绑定到端口实际上是否可以只在特定的NetworkInterface上收听?

Can anyone explain what binding to a port only actually does and is it possible to listen only on a specific NetworkInterface?

更新#1 **

Updated #1 **

到目前为止阅读了回复并与同事讨论过这个问题,以下是我对Java MulticastSocket的理解:

Having read the replies so far and discussed this with a work colleague, the following is my understanding of Java MulticastSocket:


  1. MulticastSocket()创建一个多播套接字绑定一个随机选择的端口(由主机的底层操作系统绑定到通配符地址0.0.0.0即所有网卡。但是用 null 创建一个未绑定的MulticastSocket。在这种情况下调用`bind(SocketAddress)方法绑定到IP和端口。

  2. MulticastSocket(int port)创建绑定到特定端口但在每个IP地址上的多播套接字。

  3. MulticastSocket(SocketAddress) sa)创建绑定到指定IP地址的多播套接字(可以是任何IP地址,甚至无效的多播地址)和端口。

  1. MulticastSocket() creates a multicast socket bound a port selected at random (by the host's underlying OS bound to the wildcard address 0.0.0.0 i.e. ALL network cards. However calling this constructor with null creates an unbound MulticastSocket. In this scenario calling the `bind(SocketAddress) method binds to a IP and port.
  2. MulticastSocket(int port) creates a multicast socket bound to a specific port but on EVERY IP address.
  3. MulticastSocket(SocketAddress sa) creates a multicast socket bound to the specified IP address (which could be ANY IP address, even an invalid Multicast address) and port.

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

Using option 2, this means potentially any packet sent to the specified port, regardless of its actual destination will be passed to the MulticastSocket. I say potentially because Multicast packets will only arrive if the group has been joined (but other packets destined to non-Multicast addresses will arrive provided the port number matches?)

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

Using option 3, I can bind to an IP address and only packets whose destination matches will arrive at the socket. It would be perfectly feasible with this option to bind to the IP of a particular network interface however no Multicast packets would be received because they would not be destined for the specific IP address of the network card (which is why I never saw them arrive on the code example). It is possible to also bind to a valid Multicast address but in this scenario only specific packets whose destination matches the bound multicast address would arrive at the socket, regardless of calls to joinGroup().

现在调用 joinGroup()对套接字本身 nothing ,但向底层网络系统发出IGMP请求,以确保路由器,操作系统本身等实际上从硬件启动路由指定的多播数据包并通过网络堆栈,最后到Java MulticastSocket本身。

Now the calls to joinGroup() do nothing to the socket itself but issue an IGMP request to the underlying network system to ensure that routers, the OS itself etc actually start routing the specified multicast packets up the hardware and through the network stack and finally to the Java MulticastSocket itself.

** 更新2 **
从UNIX网络引用编程,Stevens,Fenner,Rudoff:

** Update 2 ** Quoting from "UNIX Network Programming", Stevens, Fenner, Rudoff:


要接收多播数据报,进程必须加入多播
组并且它还必须绑定一个UDP套接字到一个prot数字,
将被用作发送给
组的数据报的目标端口号页。这两项操作是截然不同的,都是必需的。加入
该组告诉主机的IP层和数据链路层接收发送到该组的
多播数据报。绑定端口是
应用程序如何指定UDP,它希望接收向该端口发送
的数据报。除端口外,某些应用程序还将多播地址绑定到
套接字。这可以防止该端口可能收到的任何其他数据报
被传送到套接字。

To receive a multicast datagram, a process must join the multicast group and it must also bind a UDP socket to the prot number that will be used as destination port number for datagrams sent to the group. The two operations are distinct and both are required. Joining the group tells the host's IP layer and datalink layer to receive multicast datagrams sent to that group. Binding the port is how the application specifies to UDP that it wants to receive datagrams sent to that port. Some applications also bind the multicast address to the socket, in addition to the port. this prevents any other datagrams that might be received for that port to other unicast, broadcast or multicast addresses from being delivered to the socket.

我认为这解释了一切。

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

** Update 3 ** Just wanted to post the code I tested and the comments explains what happens with each.

/**
         * 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"));*/


推荐答案

如果在创建或绑定时未指定本地IP地址,则绑定到0.0.0.0,这意味着通过任何NIC接受输入。这通常是你想要的。

If you don't specify a local IP address when you create or bind it, it binds to 0.0.0.0, which means 'accept input via any NIC'. This is normally what you want.

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

It is possible to bind to a specific IP address, which implicitly means the corresponding NIC, but some systems such as Linux seem to expect multicast sockets, if bound, to be bound to the multicast group itself. This doesn't make any sense to me: what if you want to join another group?

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

I think the best and most portable idea is to listen at 0.0.0.0 and join via either a specific NIC, or via all NICs, one at a time. The latter is necessary in multi-homed hosts unless you are confident that the default route to the multicast group is the one you want the join request sent out on, because that's what happens if you don't specify a join interface.

这篇关于MulticastSocket构造函数和绑定到端口或SocketAddress的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆