基于UDP的设备查找

需求描述
设备为通讯管理机,在一个局域网内会有多个这样的设备,然后通过上位机,需要能查询到局域网内通讯管理机的IP地址。

思路
开发语言上位机、下位机都采用了Qt,然后准备用Udp来实现,折腾了一下午,目前测试效果还不错,基本思路如下:

  • 管理机加入到组播,监听组播报文,比如组播地址为“239.255.43.21”;
  • 上位机不加入到组播,需要查询的时候,直接给组播地址发送请求设备IP报文;
  • 管理机从组播网络接收到指令以后,直接给上位机地址(也就是报文发送地址)回复包含自己的IP地址

注意事项

  • UDP报文在有多个实体网卡或者虚拟网卡的系统上,一定要注意指定对应的网卡,否则会出现发不出来或者接收不到的情况。
  • 上位机未加入到组播,所以指定网卡,通过网卡上的IP地址即可;
  • 下位机加入到了组播,所以不能指定IP,只能在加入组播的时候,指定网卡;
  • 具体实现,请见代码示例。

代码实现

上位机实现:

MainWindow::MainWindow()
{
        m_udpSocket = new QUdpSocket(this);
        connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(OnReadPendingDatagrams()));
        m_udpSocket->bind(QHostAddress(ip), 45454);
}

void MainWindow::OnReadPendingDatagrams()
{
    while (m_udpSocket->hasPendingDatagrams())
    {
        QByteArray datagram;
        datagram.resize(m_udpSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        m_udpSocket->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);

        QDataStream in(&datagram, QIODevice::ReadOnly);
        in.setByteOrder(QDataStream::BigEndian);
        quint16 tag,cmd;
        in >> tag >> cmd;
        if(tag == 0x7878 && cmd == 2)
        {
            QString ip,mas;
            in >> ip >> mas;
            ui->textEdit->append(tr("找到装置:%1  %2").arg(ip).arg(mas));
        }
    }
}

void MainWindow::SearchDevice()
{
    quint16 tag,cmd;
    tag = 0x7878;
    cmd = 1;
    QByteArray sBuffer;
    QDataStream out(&sBuffer, QIODevice::WriteOnly);
    out.setByteOrder(QDataStream::BigEndian);
    cmd = 1;
    out << tag << cmd;
    ui->textEdit->append(tr("查找装置中…… %1").arg(m_udpSocket->writeDatagram(sBuffer, QHostAddress("239.255.43.21"), 45455)));
}

下位机实现:

MainWindow:MainWindow()
{
    m_udpSocket = new QUdpSocket(this);
    m_groupAddress = QHostAddress("239.255.43.21");
    m_udpSocket->bind(QHostAddress::AnyIPv4, 45455, QUdpSocket::DontShareAddress);
    QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();
    QNetworkInterface bindInterfac;
    foreach (QNetworkInterface interfac, interfaceList)
    {
        qDebug() << "network:" << interfac.humanReadableName();
        if(interfac.humanReadableName() == "eth0")
        {
            bindInterfac = interfac;
        }
    }
    qDebug() << "join group" << m_udpSocket->joinMulticastGroup(m_groupAddress, bindInterfac);
    connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(OnReadPendingDatagrams()));
}

void MainWindow::OnReadPendingDatagrams()
{
    while (m_udpSocket->hasPendingDatagrams())
    {
        QByteArray datagram;
        datagram.resize(m_udpSocket->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        m_udpSocket->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);

        QDataStream in(&datagram, QIODevice::ReadOnly);
        in.setByteOrder(QDataStream::BigEndian);
        quint16 tag,cmd;
        in >> tag >> cmd;
        if(tag == 0x7878 && cmd == 1)
        {
            QByteArray sBuffer;
            QDataStream out(&sBuffer, QIODevice::WriteOnly);
            out.setByteOrder(QDataStream::BigEndian);
            cmd = 2;
            out << tag << cmd
                << CXMLHelper::ConfigValue("manager_ip").toString()
                << CXMLHelper::ConfigValue("manager_mask").toString();
            qDebug() << "send:" << m_udpSocket->writeDatagram(sBuffer, sender, 45454);
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个基于QT C++和ONVIF协议的简单设备发现工具示例代码: 首先,你需要创建一个QT窗口应用程序,在mainwindow.h文件中添加以下头文件: ```c++ #include <QObject> #include <QUdpSocket> #include <QNetworkDatagram> #include <QList> #include <QDebug> #include <QThread> #include <QMutex> #include <QWaitCondition> #include <QTimer> #include <QNetworkInterface> #include <QNetworkAddressEntry> ``` 然后在mainwindow.h文件中添加以下成员变量: ```c++ private: QUdpSocket *m_udpSocket; QList<QHostAddress> m_localAddresses; QList<QHostAddress> m_onvifAddresses; QList<QHostAddress> m_onvifDevices; QMutex m_mutex; QWaitCondition m_waitCondition; QTimer *m_timer; ``` 在mainwindow.cpp文件中初始化成员变量: ```c++ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); // 初始化UDP套接字 m_udpSocket = new QUdpSocket(this); connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); // 获取本地IP地址列表 QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces(); foreach (QNetworkInterface interface, interfaces) { if (interface.flags().testFlag(QNetworkInterface::IsUp) && !interface.flags().testFlag(QNetworkInterface::IsLoopBack)) { QList<QNetworkAddressEntry> addressEntries = interface.addressEntries(); foreach (QNetworkAddressEntry addressEntry, addressEntries) { if (addressEntry.ip().protocol() == QAbstractSocket::IPv4Protocol) { m_localAddresses.append(addressEntry.ip()); } } } } // 设置ONVIF协议的IP地址和端口 m_onvifAddresses.append(QHostAddress("239.255.255.250")); m_onvifAddresses.append(QHostAddress("255.255.255.255")); m_onvifAddresses.append(QHostAddress("ff02::c")); m_onvifAddresses.append(QHostAddress("ff02::1")); m_onvifAddresses.append(QHostAddress("ff02::2")); // 初始化定时器 m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } ``` 接下来,实现onReadyRead()和onTimeout()函数: ```c++ void MainWindow::onReadyRead() { QNetworkDatagram datagram = m_udpSocket->receiveDatagram(); QHostAddress senderAddress = datagram.senderAddress(); if (m_onvifAddresses.contains(senderAddress) && datagram.data().contains("urn:schemas-xmlsoap-org:device:")) { QString data = QString(datagram.data()); QStringList lines = data.split("\r\n"); QString deviceType, friendlyName, manufacturer, modelDescription, modelName, serialNumber, presentationURL; foreach (QString line, lines) { if (line.contains("urn:schemas-upnp-org:device:")) { deviceType = line; } else if (line.contains("friendlyName")) { friendlyName = line.split(":").at(1).trimmed(); } else if (line.contains("manufacturer")) { manufacturer = line.split(":").at(1).trimmed(); } else if (line.contains("modelDescription")) { modelDescription = line.split(":").at(1).trimmed(); } else if (line.contains("modelName")) { modelName = line.split(":").at(1).trimmed(); } else if (line.contains("serialNumber")) { serialNumber = line.split(":").at(1).trimmed(); } else if (line.contains("presentationURL")) { presentationURL = line.split(":").at(1).trimmed(); } } QString deviceInfo = QString("%1\n%2\n%3\n%4\n%5\n%6\n%7\n") .arg(senderAddress.toString()) .arg(deviceType) .arg(friendlyName) .arg(manufacturer) .arg(modelDescription) .arg(modelName) .arg(serialNumber); m_mutex.lock(); m_onvifDevices.append(senderAddress); m_mutex.unlock(); } } void MainWindow::onTimeout() { m_udpSocket->writeDatagram("M-SEARCH * HTTP/1.1\r\n" "Host: 239.255.255.250:1900\r\n" "Man: \"ssdp:discover\"\r\n" "MX: 1\r\n" "ST: urn:schemas-xmlsoap-org:device:NetworkVideoTransmitter:1\r\n\r\n", QHostAddress("239.255.255.250"), 1900); } ``` 最后,在mainwindow.cpp文件的构造函数中启动定时器: ```c++ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); // ... // 启动定时器 m_timer->start(5000); } ``` 这样,你就可以在windows环境下运行该设备发现工具,以查找ONVIF协议的设备并获取其IP地址和其他信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值