2.8-局域网查找设备时有虚拟网卡干扰的编程处理方法(网络UDP广播包发不出去)

一、遇到问题的描述

在开发“局域网查找设备”这个功能时候,一般都会使用局域网广播技术。常见的问题时在编写程序的时候在自己电脑上使用没有问题,但是在用户或测试机器上,出现“无法查找到设备的问题”。使用WireShark软件监听局域网,可以发现是在自己软件查找设备的时候,没有正常发送出去局域网广播。

二、找到问题的真正原因

  1. 初步找到原因
    既然是广播没有发送出去,那就开始调试与测试,经过在多台电脑上对比测试,发现不能正常发出广播的电脑上有个虚拟网卡。而能正常使用的查找设备功能的电脑上没有任何虚拟网卡。因此推断是“虚拟网卡的影响”,为了验证是否是虚拟网卡导致的问题,因此现在把这个虚拟网卡禁用试试看。操作步骤是,选中这个虚拟网卡,然后右键选择“禁用”。这样就没有虚拟网卡的干扰了,然后使用自己软件的“查找设备功能”发现可以正常使用了,可以查找到设备。那么原因就确定了,就是因为虚拟网卡导致无法正常发出广播。
    有虚拟网卡以及禁用截图如下:
    在这里插入图片描述
  2. 深入分析虚拟网卡导致问题的原因
    观察到的问题现象是,只有一个网卡的时候,查找设备功能可以正常使用。但是当增加了一个虚拟网卡,就导致广播无法发送。进一步测试发现,如果电脑上有2张实体网卡的时候,也会导致“查找设备功能”无法使用。 因此呢,推测是自己的程序只使用了电脑上的一张默认网卡来通信(包括查找设备中用的广播)。因此呢在只有一张网卡的电脑上,可以正常运行,但是有多张网卡和有虚拟网卡的时候,默认的网卡只能是多张网卡其中的一张。因此导致自己的程序只在其中一张网卡上正常发送出了广播。而在其他网卡上没有发出任何广播。因此导致多张网卡和有虚拟网卡的时候,有可能导致连接下位的网卡无法发出广播。导致无法查找到设备。(以上结论可以使用WireShark软件验证)。
  3. 额外说明
  • 虚拟网卡的来源:电脑上安装有虚拟机软件的时候,那么你电脑上就会多出一张“虚拟网卡”
  • 虚拟网卡对虚拟机的作用:虚拟网卡是虚拟出来的一张网卡,用于虚拟机跳过寄主电脑直接接入局域网,从而使虚拟机里面的系统和寄主计算机处于同一局域网下。
  1. 多网卡导致无法查找到设备的典型案例
    在这里插入图片描述

三、解决方案

  1. 简单处理问题

原因以及明确了,在无法查找到设备电脑上,查看一下系统中的网卡。如果有虚拟网卡,把虚拟网卡右键禁用即可。
如果是因为有多张网卡导致,那么依次禁用其中一张网卡,然后验证是否可以查找到设备。最终确定一张唯一可用的网卡即可。

  1. 编程处理问题

虽然处理方法1可以快速解决问题,但是用户体验不好,给用户一种软件不稳定的感觉。同时因为这个问题也会给安装技术人员或者售后增加负担。因此静下来,从编程的角度彻底把这个问题解决掉吧。

由于问题原因都搞明白了,那么编程思路有2条。

  • 软件在查找设备之前,现在使用系统库函数确定所用电脑有几张网卡,获取这些网卡的名字,从名字上过滤掉“虚拟网卡”,只使用一张非“虚拟网卡”来发送广播。从而解决问题。(虚拟网卡的名字和普通网卡名字有明显区别)
  • 那么在电脑上有3张以及3张以上的电脑上,上面的方法就不太好了,因为非“虚拟网卡”有2张及2张以上。到底该选哪张呢?解决办法也其实也简单粗暴。即把所有非“虚拟网卡”的网卡都作为广播发送端。这样的话无论你的下位机与电脑的哪张网卡在同一个局域网下,都可以查找到设备。更简单粗暴单实用的是不区分虚拟网卡和物理网卡,让所有电脑上的网卡都发送查找设备的广播并监听下位反馈数据包。

四、实际编程

这里仅列出,在各个开发平台上怎样获取多个网卡的Ip地址。使用此IP地址作为发送广播时候指定的本地IP地址。使用不同本地地址发送广播,即可达到使用不同本机网卡发送广播的目的。
有关怎样使用不同网卡IP地址,发送广播的实例代码,请参考“2.9-局域网查找设备的实现代码(支持有虚拟网卡)”。

  1. Qt
QList<QString> MyNet::GetIpListOfComputer() {
    QList<QString> ret_list;
    QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();

    foreach(QNetworkInterface interfaceItem, interfaceList)
    {
        if(interfaceItem.flags().testFlag(QNetworkInterface::IsUp)
           &&interfaceItem.flags().testFlag(QNetworkInterface::IsRunning)
           &&interfaceItem.flags().testFlag(QNetworkInterface::CanBroadcast)
           &&interfaceItem.flags().testFlag(QNetworkInterface::CanMulticast)
           &&!interfaceItem.flags().testFlag(QNetworkInterface::IsLoopBack)
           //在这里通过名字过滤掉“虚拟网卡”
           //&&!interfaceItem.humanReadableName().contains("VMware")
           //&&!interfaceItem.humanReadableName().contains("vnic") //mac parallels
           //&&!interfaceItem.humanReadableName().contains("Npcap") 
           //在Windows下使用此过滤会导致通信出问题,因此这里采用,即便是虚拟网卡也当初正常网卡来发送广播,即所有可用监测到的网卡都用了发送广播,从而解决问题
                )
        {
 QList<QNetworkAddressEntry> addressEntryList=interfaceItem.addressEntries();
 foreach(QNetworkAddressEntry addressEntryItem,addressEntryList)
 {
                if(addressEntryItem.ip().protocol()==QAbstractSocket::IPv4Protocol)
  {
  //这里打印出电脑上的所有网卡,
  qDebug()<<"------------------------------------------------------------";
  qDebug()<<"Adapter Name:"<<interfaceItem.name();
  qDebug()<<"Adapter Address:"<<interfaceItem.hardwareAddress();
  qDebug()<<"IP Address:"<<addressEntryItem.ip().toString();
  qDebug()<<"IP Mask:"<<addressEntryItem.netmask().toString();
  qDebug() << "可过滤的网卡名字" << interfaceItem.humanReadableName();

  ret_list.append(addressEntryItem.ip().toString());
                }
            }
        }
    }

    //返回所有可用网卡的Ip地址,注:每个网卡有自己的地址(当然有的网卡其实没有接入局域网,因此虽然有ip地址,但是其实无法完成任何通信)
    return ret_list;
}
  1. C#Winform
private List<String> GetIpListOfTheNetInterface() {

    List<String> ipList = new List<string>();

    NetworkInterface[] NetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
    foreach (NetworkInterface NetworkIntf in NetworkInterfaces)
    {
        IPInterfaceProperties IPInterfaceProperties = NetworkIntf.GetIPProperties();
        UnicastIPAddressInformationCollection UnicastIPAddressInformationCollection = 
                                                IPInterfaceProperties.UnicastAddresses;
        foreach (UnicastIPAddressInformation UnicastIPAddressInformation in 
                                            UnicastIPAddressInformationCollection)
        {
            if (UnicastIPAddressInformation.Address.AddressFamily 
                                    == AddressFamily.InterNetwork)
            {
                String ip = UnicastIPAddressInformation.Address.ToString();
                if (!ip.Equals("127.0.0.1")) {
                    ipList.Add(ip);
                }
            }
        }
    }
    return ipList;
}
发布了88 篇原创文章 · 获赞 45 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/robert_cysy/article/details/104595766