java 向网卡发送广播_java (多网卡环境下)发送组播广播(multicast/broadcast)失败问题...

java发送组播或广播包并不复杂网上有很多文章,比如下面的两篇:

《Java实现组播(multicast)简单例子》

《Java 网络编程案例:使用 MulticastSocket 实现多点广播》

这些例子都大同小异,拿来就可以用,我刚开始使用组播/广播时就是这样抄个例子,编译,运行,收到消息—完美

但是,当我们的项目在开始运行时,发现问题来了:有时会收不到某台主机发送的组播包,开始以为是路由器或交换阻止组播包通过,就改为广播.改为广播后,发现问题依然存在.

经过反复测试,发现了规律,当电脑上有多块网卡(虚拟网卡也算)时,就有可能无法发出组播包数据,但自己可以收到自己发送的组播/广播包。

哇哦,原来与多网卡环境有关。有了这个规律,再去百度发现不少关于多网卡环境下发送组播/广播包问题的文章,比如这个

《解决多网卡环境下使用特定网卡广播UDP消息的问题》

这篇文章开头的内容就给出了解决办法,如下图:

3eced062a23c5675a554bb4aed4a8bde.png

看了第一条我就明白了。这是最重要的。

一般情况下,我们向一个IP地址发送数据,我们并不需要指定用哪块网卡发送,因为目标地址明确,底层网卡驱动会帮我们选择合适的网卡发送数据,

但广播或组播就不同,广播或组播地址不是一个指向单一主机的地址,在没有明确的目标指向性的情况下,底层网卡驱动会选择找到第一块网卡做为默认网卡发送数据。如果这时这个默认网卡是一个虚拟网卡(比如我的电脑上装了虚拟机就有一块虚拟网卡),那么发送组播数据就没有真的通过物理网卡发出。而只能被自己接收到。因为这个原因,对于多网卡环境下,发送广播或组播包就必须要指定用哪块网卡发送。

所以我的解决办法就是:遍历所有物理网卡,在每一块网卡上都把组播或广播数据发送一遍,接收组播包时将要明确将每一块物理网卡加入到组播地址中。

下面是我的实现代码片段:

发送组播或广播:

/**

* 向指定的组播或广播地址和端口发送组播数据

* @param group 组播或广播地址

* @param port 端口

* @param message 发送的数据

* @param ttl time-to-live for multicast packets

* @param nic 指定发送数据的网卡

* @throws IOException

*/

public static void sendMulticast(InetAddress group,int port,byte[] message, Integer ttl,NetworkInterface nic) throws IOException{

checkArgument(null != group,"group is null");

// 如果地址不是组播或广播地址则抛出异常

checkArgument(group.isMulticastAddress() || isBroadcast(group),"group %s is not a multicast or broadcast address",group);

checkArgument(message != null && message.length > 0,"message is null or empty");

checkArgument(null != nic,"nic is null");

// 获取网卡的绑定的所有IP地址枚举对象

Enumeration nifAddresses = nic.getInetAddresses();

if(!nifAddresses.hasMoreElements()){

System.out.printf("NOT ADDRESS ON %s\n",nic.toString());

return;

}

// 从网卡上绑定的IP地址创建的InetSocketAddress 对象

InetSocketAddress inetAddr= new InetSocketAddress(nifAddresses.nextElement(),0);

DatagramSocket ds = null;

try {

if(group.isMulticastAddress()){

// MulticastSocket 绑定到指定的网卡

@SuppressWarnings("resource")

MulticastSocket ms = new MulticastSocket(inetAddr);

if(ttl != null){

ms.setTimeToLive(ttl);

}

ds = ms;

}else{

// DatagramSocket绑定到指定的网卡

ds = new DatagramSocket(inetAddr);

ds.setBroadcast(true);

}

ds.send(new DatagramPacket(message, message.length,group,port));

} finally {

if(ds != null){

ds.close();

}

}

}

/**

* 向指定的组播或广播地址和端口发送组播数据

* @param group 组播或广播地址

* @param port 端口

* @param message 发送的数据

* @param ttl time-to-live for multicast packets

* @throws IOException

*/

public static void sendMulticast(InetAddress group,int port,byte[] message, Integer ttl) throws IOException{

checkArgument(null != group,"group is null");

// 如果地址不是组播或广播地址则抛出异常

checkArgument(group.isMulticastAddress() || isBroadcast(group),"group %s is not a multicast or broadcast address",group);

checkArgument(message != null && message.length > 0,"message is null or empty");

// 遍历所有物理网卡,将数据在每个网卡上发送一次

for(NetworkInterface nic:getNICs(Filter.UP,Filter.PHYICAL_ONLY)){

sendMulticast(group,port,message,ttl,nic);

}

}

/**

* 判断一个地址是否为广播地址(255.255.255.255)

* @param addr

* @return

*/

public static boolean isBroadcast(InetAddress addr){

return addr.getHostAddress().equals("255.255.255.255");

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

接收广播或组播包代码片段

/**

* socket初始化

* @return 当前对象

* @throws IOException 创建组播对象({@link MulticastSocket})时出错

*/

public MultiCastDispatcher init() throws IOException{

if(null == datagramSocket){

if(NetworkUtil.isBroadcast(group)){

datagramSocket = new DatagramSocket(port);

datagramSocket.setBroadcast(true);

}else{

MulticastSocket multicastSocket = new MulticastSocket(port);

// 遍历所有物理网卡,对每块网卡执行joinGroup

for(NetworkInterface nic:NetworkUtil.getNICs(Filter.UP,Filter.PHYICAL_ONLY)){

InetSocketAddress inetAddr= new InetSocketAddress(group,0);

multicastSocket.joinGroup(inetAddr,nic);

}

this.datagramSocket = multicastSocket;

}

}

return this;

}

/**

* 循环接收group,port指定的组播地址发送的数据并交给{@link #processor}处理

*/

@Override

public void run() {

stopListener = Boolean.FALSE;

DatagramPacket packet = new DatagramPacket(message, message.length);

try {

while(!Boolean.TRUE .equals(stopListener)){

try {

checkArgument(datagramSocket != null,"multicastSocket is uninitizlied");

datagramSocket.receive(packet);

byte[] recevied = new byte[packet.getLength()];

System.arraycopy(message, 0, recevied, 0, packet.getLength());

// 处理收到的广播/组播数据

if(!processor.apply(recevied)){

break;

}

} catch (Exception e) {

if(!onerr.apply(e)){

break;

}

}

}

} finally {

try {

if(datagramSocket instanceof MulticastSocket){

// 遍历所有物理网卡,对每块网卡执行leaveGroup

for(NetworkInterface nic:NetworkUtil.getNICs(Filter.UP,Filter.PHYICAL_ONLY)){

InetSocketAddress inetAddr= new InetSocketAddress(group,0);

((MulticastSocket) datagramSocket).leaveGroup(inetAddr,nic);

}

}

} catch (IOException e) {

e.printStackTrace();

}

datagramSocket.close();

datagramSocket = null;

stopListener = null;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值