且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

扯淡的多播参数IP_MULTICAST_LOOP

更新时间:2022-09-16 23:28:00

因为本文是用JAVA做的测试,JAVA在这个地方有一点奇怪。接口名称与实现(即文档名称是冲突),看JDK源码:


  1. /** 
  2.     * Disable/Enable local loopback of multicast datagrams 
  3.     * The option is used by the platform's networking code as a hint  
  4.     * for setting whether multicast data will be looped back to  
  5.     * the local socket. 
  6.     * 
  7.     * <p>Because this option is a hint, applications that want to 
  8.     * verify what loopback mode is set to should call  
  9.     * {@link #getLoopbackMode()} 
  10.     * @param disable <code>true</code> to disable the LoopbackMode 
  11.     * @throws SocketException if an error occurs while setting the value 
  12.     * @since 1.4 
  13.     * @see #getLoopbackMode 
  14.     */ 
  15.    public void setLoopbackMode(boolean disable) throws SocketException 

 当MulticastSocket的loopbackMode为true时,表示禁用IP_MULTICAST_LOOP。这一点与常规思路有点不同。

引用:BTW, I think sun's naming way is ugly, about socket.setLoopbackMode at least. When I see this line, I really think it is to enable socket's 
LoopbackMode if no the following comment, but actually the name of the 
parameter needed by setLoopbackMode is "disable.
先上多播通信的模拟源代码,接收端的:


  1. package com.guojje.mcast; 
  2.  
  3. import java.io.IOException; 
  4. import java.net.DatagramPacket; 
  5. import java.net.InetAddress; 
  6. import java.net.InetSocketAddress; 
  7. import java.net.MulticastSocket; 
  8.  
  9. public class MultiServer { 
  10.     public static void main(String[] args) throws IOException { 
  11.         int i = 0
  12.         InetAddress dbind = null
  13.         InetAddress gaddr = null
  14.         int port = 9090
  15.         boolean loopback = false
  16.  
  17.         while (args.length > i + 1) { 
  18.             if (args[i].equals("-b")) { 
  19.                 dbind = InetAddress.getByName(args[i + 1]); 
  20.                 i = +2
  21.             } 
  22.  
  23.             if (args[i].equals("-g")) { 
  24.                 gaddr = InetAddress.getByName(args[i + 1]); 
  25.                 i = +2
  26.             } 
  27.  
  28.             if (args[i].equals("-p")) { 
  29.                 port = Integer.parseInt(args[i + 1]); 
  30.                 i = +2
  31.             } 
  32.  
  33.             if (args[i].equals("-l")) { 
  34.                 loopback = Boolean.parseBoolean(args[i + 1]); 
  35.             } 
  36.             i += 2
  37.         } 
  38.  
  39.         if (gaddr == null) { 
  40.             gaddr = InetAddress.getByName("234.2.3.4"); 
  41.         } 
  42.         System.out.println("bind addresss: " + dbind + ", " + port); 
  43.         System.out.println("group addresss: " + gaddr); 
  44.         System.out.println("loopback: " + loopback); 
  45.         MulticastSocket ms = null
  46.  
  47.         if (dbind != null) { 
  48.             ms = new MulticastSocket(new InetSocketAddress(dbind, port)); 
  49.         } else { 
  50.             ms = new MulticastSocket(new InetSocketAddress(port)); 
  51.         } 
  52.  
  53.         ms.setLoopbackMode(loopback); 
  54.         ms.joinGroup(gaddr); 
  55.  
  56.         while (true) { 
  57.             DatagramPacket db = new DatagramPacket(new byte[100], 100); 
  58.             ms.receive(db); 
  59.             System.out.println("receive(from " + db.getSocketAddress() + "):" 
  60.                     + new String(db.getData(), 0, db.getLength())); 
  61.         } 
  62.     } 

接收端:


  1. package com.guojje.mcast; 
  2.  
  3. import java.net.DatagramPacket; 
  4. import java.net.InetAddress; 
  5. import java.net.InetSocketAddress; 
  6. import java.net.MulticastSocket; 
  7.  
  8. public class McastClient { 
  9.     public static void main(String[] args) throws Exception { 
  10.         int i = 0
  11.         InetAddress dbind = null
  12.         InetAddress gaddr = null
  13.         int port = 9091
  14.         int gport = 9090
  15.         boolean loopback = false
  16.         boolean addG = false
  17.         if (args.length > i + 1 && args[i].equals("-b")) { 
  18.             dbind = InetAddress.getByName(args[i + 1]); 
  19.             i = +2
  20.         } 
  21.  
  22.         while (args.length > i + 1) { 
  23.             if (args[i].equals("-b")) { 
  24.                 dbind = InetAddress.getByName(args[i + 1]); 
  25.                 i = +2
  26.             } 
  27.  
  28.             if (args[i].equals("-g")) { 
  29.                 gaddr = InetAddress.getByName(args[i + 1]); 
  30.                 i = +2
  31.             } 
  32.  
  33.             if (args[i].equals("-p")) { 
  34.                 port = Integer.parseInt(args[i + 1]); 
  35.                 i = +2
  36.             } 
  37.  
  38.             if (args[i].equals("-l")) { 
  39.                 loopback = Boolean.parseBoolean(args[i + 1]); 
  40.  
  41.             } 
  42.             i += 2
  43.         } 
  44.  
  45.         if (gaddr == null) { 
  46.             gaddr = InetAddress.getByName("234.2.3.4"); 
  47.         } 
  48.         System.out.println("bind addresss: " + dbind + ", " + port); 
  49.         System.out.println("group addresss: " + gaddr + "," + gport); 
  50.         System.out.println("loopback: " + loopback); 
  51.  
  52.         MulticastSocket ms = null
  53.         if (dbind != null) { 
  54.             ms = new MulticastSocket(new InetSocketAddress(dbind, port)); 
  55.         } else { 
  56.             ms = new MulticastSocket(new InetSocketAddress(port)); 
  57.         } 
  58.         ms.setLoopbackMode(loopback); 
  59.  
  60.         if (addG) { 
  61.             ms.joinGroup(gaddr); 
  62.         } 
  63.         DatagramPacket dp = new DatagramPacket("hello!!".getBytes(), 7
  64.                 new InetSocketAddress(gaddr, gport)); 
  65.         while (true) { 
  66.             ms.send(dp); 
  67.             Thread.sleep(2000); 
  68.         } 
  69.     } 

 在Windows平台:
分别启动服务端,客户端在同一台Windows机器上:
java  -cp . com.guojje.mcast.MultiServer -l false
java  -cp . com.guojje.mcast. McastClient -p 7800 –l false

发现服务端输出收到包: 
receive(from /192.168.1.62:7800):hello!!
receive(from /192.168.1.62:7800):hello!!
receive(from /192.168.1.62:7800):hello!!
receive(from /192.168.1.62:7800):hello!!
说明多播通信是正常的。
 
下一步我们把服务端启动脚本改为
java  -cp . com.guojje.mcast.MultiServer -l true 
即禁用IP_MULTICAST_LOOP,发现服务端无法收到数据包。

我们再测试客户:
java  -cp . com.guojje.mcast. McastClient -p 7800 –l true.
发现对于发送端来说,IP_MULTICAST_LOOP为true 或false对结果没有影响。

那么linux是否也如此呢。

我们首先清空linux的IPV6环境,查看IPV6模块是否装载:
[root@localhost ~]# lsmod | grep ipv6
ipv6                  251137  18
发现存在IPV6模块,则关闭IPV6:
使用vi编辑器,打开/etc/modprobe.conf 
.在文档中加入如下的两条:
alias net-pf-10 off
alias ipv6 off
重启机器。

查看多播是否启动:
[root@localhost javawork]# route -e
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
192.168.1.0     *               255.255.255.0   U         0 0          0 eth0
169.254.0.0     *               255.255.0.0     U         0 0          0 eth0
default         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
发现没有多播路由,增加多播路由:
[root@localhost javawork]# route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

难证一下:
[root@localhost javawork]# route -e
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
192.168.1.0     *               255.255.255.0   U         0 0          0 eth0
169.254.0.0     *               255.255.0.0     U         0 0          0 eth0
224.0.0.0       *               240.0.0.0       U         0 0          0 eth0
default         192.168.1.1     0.0.0.0         UG        0 0          0 eth0

[root@localhost ~]# netstat -gn
IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
lo              1      224.0.0.1
eth0            1      224.0.0.251
eth0            1      224.0.0.1
OK,开始测试。

分别启动服务端,客户端在同一台linux相器上:
java  -cp . com.guojje.mcast.MultiServer –b 234.2.3.4
java  -cp . com.guojje.mcast. McastClient -p 7800

这里要注意与Windows系统有不同,MulticastSocket Server端绑定的地址必须是多播或者不指定,否则收不到多播包。
参考我的另一篇文章:http://guojuanjun.blog.51cto.com/277646/746408

这里出现另一个奇怪的现象,对于服务端
java  -cp . com.guojje.mcast.MultiServer –b 234.2.3.4  -l true / false
IP_MULTICAST_LOOP为true 或false对结果没有影响。但客户端出怪事了。
java -cp . com.guojje.mcast.McastClient  -b 192.168.1.90 -l true
当IP_MULTICAST_LOOP为true,即禁用loopbackmode时,服务端无法收到多播包.

从msdn上找到这样一段话(Sun的bug database也有相应的引用):

Note  The Winsock version of the IP_MULTICAST_LOOP option is semantically different than the UNIX version of the IP_MULTICAST_LOOP option:

    In Winsock, the IP_MULTICAST_LOOP option applies only to the receive path.
    In the UNIX version, the IP_MULTICAST_LOOP option applies to the send path.

For example, applications ON and OFF (which are easier to track than X and Y) join the same group on the same interface; application ON sets the IP_MULTICAST_LOOP option on, application OFF sets the IP_MULTICAST_LOOP option off. If ON and OFF are Winsock applications, OFF can send to ON, but ON cannot sent to OFF. In contrast, if ON and OFF are UNIX applications, ON can send to OFF, but OFF cannot send to ON.

 

意思是说在windows平台,  IP_MULTICAST_LOOP 应用到接收路径. 是否可以理解为在接收端启用IP_MULTICAST_LOOP.
在Linux平台,  IP_MULTICAST_LOOP 应用到发送路径. 是否可以理解为在发送端端启用IP_MULTICAST_LOOP 即可。.

目前来说,这样理解是通的。

以上测试是基于IPv4平台,对于IPV6又将如何呢?
首先在Windows xp上进行IPv6的配置,目前Windows上只有Link-local,分别启动服务端,客户端在同一台windows 相器上:
java  -cp . com.guojje.mcast.MultiServer –b fe80::224:1dff:fe2a:689a%4 –g ff01::1
java  -cp . com.guojje.mcast. McastClient  –gp ff01::1 –b fe80::224:1dff:fe2a:689a%4 

却发现只有发送端IP_MULTICAST_LOOP为启用时,接收端才能收到多播数据,与linux在IPv4上的表现如一辙,Windows这种IPv4与IPv6对于IP_MULTICAST_LOOP的语义出现错位,真让人大跌眼镜。太扯淡了。

添加Global地址进行测试:
D:\javawork\NIO_SSL\bin>netsh interface ipv6 add address "本地连接" 2001:3C8:1205::4
确定。

分别启动服务端,客户端在同一台windows 相器上:
java  -cp . com.guojje.mcast.MultiServer –b 2001:3C8:1205::4
java  -cp . com.guojje.mcast. McastClient -p 7800 –b 2001:3C8:1205::4

没有区别,和只存在link-local是一致的:只有发送端IP_MULTICAST_LOOP为启用时,接收端才能收到多播数据,看来对于IPv6, Windows已完全倒向Linux对于IP_MULTICAST_LOOP的语义。

那么对于Linux 对于IPv6,又将如何呢?先测试只有link-local地址的情况:
java  -cp . com.guojje.mcast.MultiServer –p32770 –g ff01::1
java  -cp . com.guojje.mcast. McastClient -gp 32770 –g ff01::1

发现与Linux pv4一样,只有发送端IP_MULTICAST_LOOP为启用时,接收端才能收到多播数据。而服务端启用与否都不重要。

在Linux上在测试link-local方式的多播时,发现端口号需要大于32770才可能通信。不知道是什么原因。(后来证明是IPv6的防火墙问题,chkconfig ip6tables off关闭即可)

至于Linux上,IPv6 Gloal地址的多播通信,我想情况一定与Windows是一致的。暂不做测试了。



本文转自 anranran 51CTO博客,原文链接:http://blog.51cto.com/guojuanjun/876489