需求描述
设备为通讯管理机,在一个局域网内会有多个这样的设备,然后通过上位机,需要能查询到局域网内通讯管理机的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);
}
}
}