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

我可能对绑定这个术语有一个基本的误解,但我对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调用此构造函数会创建一个未绑定的MulticastSocket。在这种情况下调用`bind (SocketAddress)方法绑定到IP和端口。
  2. MulticastSocket(int port)创建绑定到特定端口但在每个IP地址上的多播套接字。
  3. MulticastSocket(SocketAddress sa)创建绑定到指定IP地址(可能是任何IP地址, 甚至是无效的多播地址)和端口的多播套接字。

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

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

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

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

要接收多播数据报,进程必须加入多播组,并且还必须 UDP套接字绑定到将用作发送到该组的数据报的目标端口号的prot编号。 这两项操作是截然不同的,都是必需的。 加入该组会告知主机的IP层和数据链路层接收发送到该组的多播数据报。 绑定端口是应用程序如何指定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 aa 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 aa 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 (ie 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接受输入”。 这通常是你想要的。

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

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

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

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