一、前言
以下代码实现了udp组播的接收和发送数据的功能。但是,在多网卡环境下仍然存在部分问题,但也解决了大部分问题。具体的内容在下面叙述。
二、大致步骤
QT += network
.pro文件添加network
#include <QUdpSocket>
导入头文件
QUdpSocket* udpSocket = new QUdpSocket;
new个对象
udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,1);
设置Socket为组播模式
udpSocket->bind(QHostAddress::AnyIPv4,目标端口,QUdpSocket::ShareAddress);
绑定目标IP,端口
QUdpSocket::ShareAddress ;
允许其他服务绑定相同 端口 IP
udpSocket->setMulticastInterface(const QNetworkInterface &iface);
解决多网卡问题
udpSocket->joinMulticastGroup(groupaddress);
加入组播
connect(udpSocket,&QUdpSocket::readyRead,this,&TRDP::receiveMessage);
连接接收函数 receiveMessage为自定义接收槽函数
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
预分配容量udpSocket->readDatagram(datagram.data(),datagram.size(),目标IP地址,目标端口);
UDP读取数据 (地址和端口传入类型为指针或&)
udpSocket->writeDatagram(str.data(),str.length(), 目标IP, 目标port);
//发送数据 QByteArray str 发送数据
udpSocket->leaveMulticastGroup(groupaddress);
//退出组播
三、具体实现
1.加入组播
定义两个全局变量,方便使用
quint16 port; //目标端口
QHostAddress groupaddress; //目标IP
我的ip和端口是通过界面输入的:
void XXX::on_StartBt_clicked()
{
port = ui->TargetPortBox->text().toUInt(); //获取目标端口号
QString ip = ui->TargetIP->text(); //获取目标IP地址
groupaddress = QHostAddress(ip); //类型转换
udpSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption,1); //设置Socket为组播模式
bool ok = udpSocket->bind(QHostAddress::AnyIPv4,port,QUdpSocket::ShareAddress); //绑定目标IP,端口
QList<QNetworkInterface> list = QNetworkInterface::allInterfaces(); //获取系统里所有的网络接口
foreach(QNetworkInterface intf, list){ //遍历所有接口
//intf.addressEntries()返回此接口拥有的IP地址列表及其相关的网掩码和广播地址。
foreach(QNetworkAddressEntry entry, intf.addressEntries()){
if (entry.broadcast() != QHostAddress::Null && entry.ip() != QHostAddress::LocalHost && entry.ip().protocol() == QAbstractSocket::IPv4Protocol){
udpSocket->setMulticastInterface(intf);
}
}
}
if(ok){
bool isTrue = udpSocket->joinMulticastGroup(groupaddress); //绑定成功则加入组播,反之关闭socket
if(!isTrue) QMessageBox::warning(NULL,"ERROR","UDP加入组播失败");
//else {
//_timer.start(10000);
//}
}
else {
QMessageBox::warning(NULL,"ERROR","UDP IP、端口连接失败");
udpSocket->close();
}
}
通过 udpSocket->setMulticastInterface(intf);
即使是在多网卡环境下,依然可以收到数据。但是,我发现在有些电脑上,我只有在发送了一条任意数据后才能接收到数据,没找到具体原因。所有我用了一个取巧的方法,在加入组播成功的同时开启一个定时器,当我接收到数据时重新设置定时器。这样在规定时间内能接受到数据定时器就不会触发。
2.接收数据
要想接收到数据,这条信号槽函数必不可少
connect(udpSocket,&QUdpSocket::readyRead,this,&TRDP::receiveMessage); //连接接收函数
void XXX::receiveMessage()
{
while(udpSocket->hasPendingDatagrams()){ //等待读取数据
QByteArray datagram;
QHostAddress targetAddress = groupaddress; //从目标IP读数据
quint16 targerPort = port; //从目标端口读取数据
datagram.resize(udpSocket->pendingDatagramSize()); //预分配容量
udpSocket->readDatagram(datagram.data(),datagram.size(),&targetAddress,&targerPort); //UDP读取数据
//_timer.start(10000);
}
读取数据没什么难得,步骤基本上就是这么个步骤。
3.发送数据
发送数据也没什么难的,将需要发送得数据根据协议进行封包就行,没有协议就更简单。
void XXX::slot_control_send(int count)
{
QString tem = QString("%1%2%3%4%5%6%7%8%9%10").arg("XX").arg("XX").arg("XX").arg("XX").arg("XX").arg("XX").arg("XXXX").arg("XXXX").arg(count,4,16,QLatin1Char('0')).arg("0000000000000000");
QByteArray str = data_packet_function(tem); //对数据进行TRDP封包
udpSocket->writeDatagram(str.data(),str.length(), groupaddress, port); //发送数据
}
四、总结
在多网卡环境下,基本能解决接收不到数据得问题,但也会存在一些问题,问题如下:
1. 会遇到在某些电脑上,存在能正常加入组播,但在发送数据下去得情况下,不会主动接收数据,只有在发送数 据后才会取接收数据,甚至会在一段时间后就停止接收数据了,需要再次发送数据,如此反复。
2. 在某些电脑上,会存在使用抓包软件也抓不到数据得情况,这就导致必然接收不到数据,但是能抓到我发送下去的数据。
.
针对第1个问题:因为我这边基本上只需要接收数据,10s内至少会接收到一条数据,是针对数据解析得,而且对数据得完整性并不要求,所以在面对第一个问题时,我可以用一个取巧得办法,通过定时器在一段时间后接收不到数据,就触发发送一条数据,这个办法有很大得局限性;但需要接收到每一条数据时,这个方法就会导致数据遗漏。
第2个问题目前并没有找到原因。我用的wireshark抓包,抓不到指定端口、IP地址的数据的数据,但能抓到我自己发送下去的数据,我猜测可能是电脑中的某些杀毒软件或防火墙将数据拦截了。