MulticastSocket构造函数和绑定到端口或SocketAddress

时间:2013-10-15 22:28:32

标签: java multicast multicastsocket

我可能对绑定这个术语有一个基本的误解,但我对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),它就会停止工作。看起来我只能使用bind-to-port构造函数才能工作,因此在调用此构造函数时它会完全绑定到什么,因为我在此阶段尚未指定网络适配器。 (我知道我稍后会加入具有特定NetworkInterface的组,但是如何确保在构造函数调用期间它不绑定到任何适配器?)

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

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

已更新#1 **

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

  1. MulticastSocket()创建一个多播套接字绑定一个随机选择的端口(由主机的底层操作系统绑定到通配符地址0.0.0.0即所有网卡。但是使用 null 调用此构造函数会创建一个unbound MulticastSocket。在这种情况下,调用`bind(SocketAddress)方法绑定到IP和端口。
  2. MulticastSocket(int port)创建绑定到特定端口但在每个IP地址上的多播套接字。
  3. MulticastSocket(SocketAddress sa)创建绑定到指定IP地址的多播套接字(可以是任何IP地址,甚至无效的多播地址)和端口。
  4. 使用选项2,这意味着可能发送到指定端口的任何数据包,无论其实际目的地是否将传递给MulticastSocket。我之所以说可能是因为多播数据包只会在该组加入后才会到达(但是如果端口号与相匹配,那么发往非多播地址的其他数据包将到达?)

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

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

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

      

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

    我认为这解释了一切。

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

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

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

我认为您错过了地址绑定的重点:

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是保留的,不应使用。